nflow
Version:
event/data/control flow
1,519 lines (1,335 loc) • 88 kB
JavaScript
(function(e, a) { for(var i in a) e[i] = a[i]; }(this, /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {'use strict';
var _nflow = __webpack_require__(1);
var _nflow2 = _interopRequireDefault(_nflow);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
if (global) global.nflow = global.nFlow = _nflow2['default']; // nFlow is now deprecated
module.exports = _nflow2['default'];
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _factory = __webpack_require__(2);
var _factory2 = _interopRequireDefault(_factory);
var _consts = __webpack_require__(5);
var _logger = __webpack_require__(8);
var _logger2 = _interopRequireDefault(_logger);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var root = (0, _factory2['default'])(_consts.DEFAULTS, 'flow', []);
_logger2['default'].init(root);
exports['default'] = root;
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _behaviours = __webpack_require__(3);
var _behaviours2 = _interopRequireDefault(_behaviours);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
exports['default'] = function (defaults, name, data) {
/* jshint ignore:start */
/**
* nflow library
* @class flow
*
* @example
* let a = nflow.create('a')
* let b = nflow.create('b')
*
* let c = a.create('c')
* let d = a.create('d')
*/
/* jshint ignore:end */
var flow = defaults.factory();
defaults.behaviours.forEach(function (d) {
_behaviours2['default'][d](flow, defaults, name, data);
});
return flow;
};
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _cancellable = __webpack_require__(4);
var _cancellable2 = _interopRequireDefault(_cancellable);
var _connect = __webpack_require__(9);
var _connect2 = _interopRequireDefault(_connect);
var _create = __webpack_require__(10);
var _create2 = _interopRequireDefault(_create);
var _emit = __webpack_require__(11);
var _emit2 = _interopRequireDefault(_emit);
var _identify = __webpack_require__(17);
var _identify2 = _interopRequireDefault(_identify);
var _listen = __webpack_require__(18);
var _listen2 = _interopRequireDefault(_listen);
var _loggable = __webpack_require__(19);
var _loggable2 = _interopRequireDefault(_loggable);
var _stateful = __webpack_require__(20);
var _stateful2 = _interopRequireDefault(_stateful);
var _stats = __webpack_require__(21);
var _stats2 = _interopRequireDefault(_stats);
var _disposable = __webpack_require__(22);
var _disposable2 = _interopRequireDefault(_disposable);
var _namespace = __webpack_require__(23);
var _namespace2 = _interopRequireDefault(_namespace);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
exports['default'] = {
cancellable: _cancellable2['default'],
connect: _connect2['default'],
create: _create2['default'],
disposable: _disposable2['default'],
emit: _emit2['default'],
identify: _identify2['default'],
listen: _listen2['default'],
loggable: _loggable2['default'],
stateful: _stateful2['default'],
stats: _stats2['default'],
namespace: _namespace2['default']
};
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _consts = __webpack_require__(5);
var _utils = __webpack_require__(6);
exports['default'] = function (flow, defaults, name) {
/**
* Cancel the current {@link flow} node.
*
* Cancelling has the following effects:
* - Cancelled nodes cannot receive events.
* - Cancelled nodes cannot emit events.
* - Cancelled nodes cannot propagate events.
* - <b>All</b> child nodes of a cancelled node <b>are also cancelled recursively</b>.<br>
*
* Cancellation is final, cancelled nodes cannot be un-cancelled.
* @method
* @memberof module:flow
* @return {flow} flow - the current {@link flow} node
* @liveexample
* let foo = nflow.create('foo')
* .on('hello', cb)
*
* foo.cancel()
* @emits 'flow.cancel'
* @emits 'flow.children.cancel'
* @emits 'flow.parent.cancel'
*/
flow.cancel = function () {
(0, _utils.assert)(arguments.length, _consts.ERRORS.invalidCancelArgs);
var previousValue = flow.cancel.value;
flow.cancel.value = true;
/**
*
* Dispatched when a node has been cancelled.
* @event 'flow.cancel'
* @property {flow} flow - the node to be cancelled.
* @see flow.cancel
* @example
* nflow.create('timer-service')
* .on('flow.cancel', stopTimer)
*/
/**
*
* Dispatched when one of the node's parents has been cancelled.
* @event 'flow.parent.cancel'
* @property {flow} flow - the node to be cancelled.
* @see flow.cancel
*/
/**
*
* Dispatched when one of the node's children(recursive) has been cancelled.
* @internal
* @event 'flow.children.cancel'
* @property {flow} flow - the node to be cancelled.
* @see flow.cancel
*/
(0, _utils.dispatchInternalEvent)(flow, 'cancel', true, previousValue);
return flow;
};
flow.cancel.value = false;
/**
* @memberof module:flow
* @readonly
* @return {Boolean} `true` if the node or any of the node's parents have been cancelled, else `false`
*/
flow.isCancelled = function () {
return [flow].concat(flow.parents()).some(function (e) {
return e.status() === _consts.STATUS.CANCELLED || e.status() === _consts.STATUS.DISPOSED;
});
};
/**
* Stop or augments propagation of the emitted event.
*
* If the method is called with no parameters, the event will not be delivered to other listeners.
* ```
* .on('price-update', function(){
* this.stopPropagation() // no further listeners will receive the event
* })
* ```
* If a {@link flow.DIRECTION|direction} is given, the event propagation gets restricted in the given direction.
* ```
* foo.on('price-update', function(){
* // child nodes of `foo` will not receive the `price-update` event
* this.stopPropagation('DOWNSTREAM')
* })
* ```
* @tutorial propagation
* @see flow.propagationStopped
* @param {DIRECTION} [direction] Optional direction for augmenting the event propagation
* @return {flow} flow - the current {@link flow} node
* @emits 'flow.propagationStopped'
* @emits 'flow.children.propagationStopped'
* @emits 'flow.parent.propagationStopped'
* @emits 'flow.propagationAugmented'
* @emits 'flow.children.propagationAugmented'
* @emits 'flow.parent.propagationAugmented'
*/
flow.stopPropagation = function () {
var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _consts.UNSET;
direction !== _consts.UNSET && (0, _utils.assert)(!_consts.DIRECTION[direction.toUpperCase()], _consts.ERRORS.invalidStopPropagationArgs);
if (direction === _consts.UNSET) {
flow.status.set(_consts.STATUS.STOPPED);
flow.emit.targets = [];
flow.stopPropagation.value = true;
flow.stopPropagation.modifiers[flow.target.guid] = -1; // bitmask fill
/**
*
* Dispatched when a dispatched event's propagation has been stopped.
* @event 'flow.propagationStopped'
* @property {flow} flow - the node that has stopped propagating
* @see flow.stopPropagation
* @example
* nflow.create('timer-service')
* .on('flow.propagationStopped', cb)
*/
/**
*
* Dispatched when one of the node's parents' propagation has been stopped.
* @event 'flow.parent.propagationStopped'
* @property {flow} flow - the node that has stopped propagating.
* @see flow.stopPropagation
*/
/**
*
* Dispatched when one of the node's children(recursive) has stopped propagating.
* @internal
* @event 'flow.children.propagationStopped'
* @property {flow} flow - the node that has stopped propagating
* @see flow.stopPropagation
*/
(0, _utils.dispatchInternalEvent)(flow, 'propagationStopped', true);
} else {
var d = _consts.DIRECTION[direction.toUpperCase()];
/**
*
* Dispatched when a dispatched event's propagation has been augmented.
* @event 'flow.propagationAugmented'
* @property {flow} flow - the affected node
* @property {object} changes - The changes applied to the emitted event
* @property {DIRECTION} changes.direction - The changes applied to the emitted event
* @property {flow} changes.target - The node that augmented the event flow
* @see flow.stopPropagation
*
*/
/**
*
* Dispatched when one of the node's parents' propagation has been augmented.
* @event 'flow.parent.propagationAugmented'
* @property {flow} flow - the affected node.
* @property {object} changes - The changes applied to the emitted event
* @property {DIRECTION} changes.direction - The changes applied to the emitted event
* @property {flow} changes.target - The node that augmented the event flow
* @see flow.stopPropagation
*/
/**
*
* Dispatched when the propagation of one of the node's children(recursive) has been augmented.
* @internal
* @event 'flow.children.propagationAugmented'
* @property {flow} flow - the affected node.
* @property {object} changes - The changes applied to the emitted event
* @property {DIRECTION} changes.direction - The changes applied to the emitted event
* @property {flow} changes.target - The node that augmented the event flow
* @see flow.stopPropagation
*/
(0, _utils.dispatchInternalEvent)(flow, 'propagationAugmented', {
direction: d,
target: flow.target
});
flow.stopPropagation.modifiers[flow.target.guid.value] |= _consts.DIRECTION_BITMASK[d];
}
return flow;
};
flow.stopPropagation.value = false;
flow.stopPropagation.modifiers = {};
createStopPropagationModifiers(flow);
/**
* @readonly
* @tutorial propagation
* @see flow.stopPropagation
* @return {Boolean} `true` if the propagation was completely stopped, else `false` (even if the propagation was augmented).
*
*/
flow.propagationStopped = function () {
return flow.stopPropagation.value;
};
};
/*
* create directional (eg. `flow.stopPropagation.dowsntream(...)`) API
*/
function createStopPropagationModifiers(flow) {
Object.keys(_consts.DIRECTION).forEach(function (direction) {
flow.stopPropagation[direction] = flow.stopPropagation[direction.toLowerCase()] = function () {
return flow.stopPropagation(direction);
};
});
}
/***/ },
/* 5 */
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var UNSET = exports.UNSET = {};
/**
* Enum for the direction event propagation
* @enum {String}
* @readonly
* @name DIRECTION
* @property {String} CURRENT - the event is only propagated to the current node
* @property {String} DEFAULT - the event bubbles up to root parent(the parent that has no other parents), then traverses **depth-first** to all child nodes
* @property {String} UPSTREAM - The event bubbles up to the root node
* @property {String} DOWNSTREAM - the event traverses **depth-first** all child nodes
*/
var DIRECTION = exports.DIRECTION = {
CURRENT: 'CURRENT',
NONE: 'CURRENT', // deprecated
DEFAULT: 'DEFAULT',
UPSTREAM: 'UPSTREAM',
DOWNSTREAM: 'DOWNSTREAM'
};
var DIRECTION_BITMASK = exports.DIRECTION_BITMASK = {
CURRENT: 1 << 0,
NONE: 1 << 0, // 1 deprecated
DEFAULT: 1 << 1, // 2
UPSTREAM: 1 << 2, // 4
DOWNSTREAM: 1 << 3 // 8
};
/**
* Enum for decribing the status of a flow node.
*
* Use {@link flow.status}() to get the status of a flow instance.
* @enum
* @readonly
* @property {String} ACTIVE (Default) The node can emit(or be emitted), propagate or listen to events.
* @property {String} FLOWING The node has been emitted(so it is now treated as an event), but it hasn't been delivered to all listeners yet.
* @property {String} STOPPED The event propagation was stopped. See {@link flow.stopPropagation}
* @property {String} COMPLETED The event has been propagated to all listeners.
* @property {String} CANCELLED The event has been cancelled. See {@link flow.cancel}
* @property {String} DISPOSED The event has been disposed. See {@link flow.dispose}
*/
var STATUS = exports.STATUS = {
IDLE: 'IDLE',
FLOWING: 'FLOWING',
STOPPED: 'STOPPED',
COMPLETED: 'COMPLETED',
CANCELLED: 'CANCELLED',
DISPOSED: 'DISPOSED'
};
var DEFAULTS = exports.DEFAULTS = {
factory: function factory() {
return {};
},
behaviours: ['identify', 'stateful', 'connect', 'create', 'disposable', 'emit', 'listen', 'cancellable', 'loggable', 'stats', 'namespace'],
direction: DIRECTION.DEFAULT
};
var ERRORS = exports.ERRORS = {
invalidGuid: 'Invalid Argument. Guid-s are immutable. Please use the .name() API to change the name of a flow object.',
invalidChildren: 'Invalid Argument. Please use child.parent(parent) to re-parent flow objects.',
invalidListener: 'Invalid Arguments. Expected a String as the listenerName, got: %s',
invalidListenerType: 'Invalid Listener function. Expected a function, got: %s',
invalidEventName: 'Invalid Arguments. Please use .emit("foo", payload) to emit a flow event.',
invalidName: 'Invalid flow Name. Expected a String value, got: %s',
invalidParent: 'Invalid flow parent object. Expected a flow instance, got: %s',
invalidParents: 'Invalid Argument. Please use the child.parent(parent) API to re-parent flow objects.',
invalidStatus: 'Invalid Argument. The .status() API is read only',
invalidNamespaceArgs: 'Invalid Argument. The .namespace() API is read only',
invalidFullNamespace: 'Invalid Argument. The .namespace.match(full_ns) requires a full namespace array, got: %s',
invalidDisposeArgs: 'Invalid Argument. The .dispose() API requires no parameters',
invalidCancelArgs: 'Invalid Argument. The .cancel() API requires no parameters',
invalidStopPropagationArgs: 'Invalid Argument. The .stopPropagation(direction) API requires either no parameters or a valid flow direction(eg. flow.direction.UPSTREAM)',
invalidStopPropagationAugmentation: 'Invalid Argument. The .stopPropagation(direction) API can only be used on emitted events.',
invalidRoot: 'Invalid Argument. The .parents.root() API is read only',
invalidStatsArgs: 'Invalid Argument. The .stats() API requires an object'
};
var NS_SEPARATOR = exports.NS_SEPARATOR = ':';
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.serialise = undefined;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _serialiser = __webpack_require__(7);
Object.defineProperty(exports, 'serialise', {
enumerable: true,
get: function get() {
return _serialiser.serialise;
}
});
exports.assert = assert;
exports.isFlow = isFlow;
exports.isInternal = isInternal;
exports.createMatcher = createMatcher;
exports.getLocalName = getLocalName;
exports.detach = detach;
exports.isDetached = isDetached;
exports.flatten = flatten;
exports.merge = merge;
exports.invalidateListenerCache = invalidateListenerCache;
exports.dispatchInternalEvent = dispatchInternalEvent;
var _factory = __webpack_require__(2);
var _factory2 = _interopRequireDefault(_factory);
var _consts = __webpack_require__(5);
var _logger = __webpack_require__(8);
var _logger2 = _interopRequireDefault(_logger);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
/*
* utils
*/
function assert(condition, error, val) {
if (condition) {
throw new Error(error.replace('%s', val));
}
return condition;
}
function isFlow(flow) {
return flow && flow.name && flow.name.isFlow;
}
function isInternal(flow) {
return flow && flow.name && flow.name.isInternal;
}
function createMatcher(matcher) {
// match ALL
if (matcher === true) return function () {
return true;
};
// match NONE
if (matcher === null || matcher === undefined || matcher === false) return function () {
return false;
};
// match by name
if (typeof matcher === 'string') {
return function (f) {
return f.name() === matcher;
};
}
// match by regex
if ((typeof matcher === 'undefined' ? 'undefined' : _typeof(matcher)) === 'object' && matcher.test) return function (f) {
return matcher.test(f.name());
};
// match by instance
if (isFlow(matcher)) return function (f) {
return f === matcher;
};
// custom matcher
return matcher;
}
/*
* returns the Local Name fragment of a namespaced listener or node name.
* @example
* 'a:b:foo' -> 'foo'
* @param {String} name Listener or node name
* @return {String} the local name segment
*/
function getLocalName(name) {
return name.split(_consts.NS_SEPARATOR).pop();
}
function detach(flow) {
flow.parent() && flow.parent().children.detach(flow);
}
function isDetached(flow) {
return !flow.parent() || !flow.parent().children.has(flow);
}
function flatten(array) {
return [].concat.apply([], array);
}
function merge(source, target) {
Object.keys(source).forEach(function (key) {
target[key] = source[key];
});
}
function invalidateListenerCache(flow) {
if (!flow) return;
updateListenerCache(flow);
flow.parents().forEach(updateListenerCache);
}
function updateListenerCache(node) {
node.on.listenerCache = {};
for (var key in node.on.listenerMap) {
node.on.listenerCache[getLocalName(key)] = true;
}
node.children.value.forEach(function (f) {
for (var _key in f.on.listenerCache) {
node.on.listenerCache[getLocalName(_key)] = true;
}
});
}
function dispatchInternalEvent(flow, name, newData, oldData) {
var currentOnly = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
if (isIgnored(flow)) return;
if (isFlow(newData) && newData.name.isInternal) return;
_logger2['default'].log(flow, name, newData, oldData);
var current = (0, _factory2['default'])(_consts.DEFAULTS, 'flow.' + name);
current.name.isInternal = true;
current.data.value = [newData, oldData];
current.parent.value = flow;
current.emit.current();
if (currentOnly) return;
var up = (0, _factory2['default'])(_consts.DEFAULTS, 'flow.children.' + name);
up.name.isInternal = true;
up.data.value = [flow, newData, oldData];
up.parent.value = flow.parent();
up.emit.upstream();
var down = (0, _factory2['default'])(_consts.DEFAULTS, 'flow.parent.' + name);
down.name.isInternal = true;
down.data.value = [flow, newData, oldData];
flow.children.value.forEach(function (f) {
return f.emit.downstream(down);
});
}
function isIgnored(flow) {
return flow.name.isInternal || [flow].concat(flow.parents()).some(function (e) {
return e.stats.value.ignore;
});
}
/***/ },
/* 7 */
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var serialise = exports.serialise = function serialise(o) {
return JSON.stringify(o, replacer());
};
var RECURSION_LIMIT = 1024;
function replacer() {
var stack = [];
var r = 0;
var i = void 0;
return function replacer(key, value) {
if (key === '') {
stack = [];
r = 0;
}
var val = parseValue(value);
if (val !== undefined) return val;
if (!value || RECURSION_LIMIT < ++r) return undefined;
i = stack.indexOf(value);
if (i < 0) return stack.push(value) && value;
return '*Recursive' + i;
};
}
function parseValue(value) {
switch (typeof value === 'undefined' ? 'undefined' : _typeof(value)) {
case 'function':
return ''.concat('function ', value.name || 'anonymous', '(', Array(value.length + 1).join(',arg').slice(1), ')');
case 'boolean':
case 'number':
case 'string':
return value;
}
}
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _utils = __webpack_require__(6);
var _consts = __webpack_require__(5);
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); } }
var loggers = [];
function log(flow, name, newData, oldData) {
if (!(0, _utils.isInternal)(flow)) {
loggers.forEach(function (f) {
f.isRemote ? f.logger(remoteLog(flow, name, newData, oldData), flow) : f.logger(flow, name, newData, oldData);
});
debug(flow, name, newData, oldData);
}
}
function debug(flow, name, newData, oldData) {
typeof global !== 'undefined' && global.__nflow_devtools_hook__ && global.__nflow_devtools_hook__(remoteLog(flow, name, newData, oldData), flow);
}
var propMap = {
'start': ['version', 'status'],
'cancel': ['status'],
'create': ['status'],
'emitted': ['recipients']
};
/*
* Converts a local log message(direct references) to a remote one(unmarshallable)
*/
function remoteLog(flow, name, d, d0) {
var o = {
flow: flow.toObj('name', 'guid'),
action: name
};
var props = ['name', 'guid'];
propMap[name] && props.push.apply(props, _toConsumableArray(propMap[name]));
if (name === 'create' && d.data() !== undefined) props.push('data');
var newData = d && d.toObj ? d.toObj.apply(d, props) : (0, _utils.serialise)(d);
var oldData = d0 && d0.toObj ? d0.toObj.apply(d0, props) : (0, _utils.serialise)(d0);
if (newData !== undefined) o.d = newData;
if (oldData !== undefined) o.d0 = oldData;
return o;
}
function init(flow) {
flow.enableDevTools = function () {
var enabled = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
console.warn('flow.enableDevtools() is now deprecated. nflow-devtools will automatically start logging when Chrome devtools is open');
return flow;
};
flow.logger = function () {
var logger = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _consts.UNSET;
var isRemote = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (logger === _consts.UNSET) return loggers;else loggers.push({ logger: logger, isRemote: isRemote });
return flow;
};
flow.logger.reset = function () {
loggers = [];
};
debug(flow, 'start', flow);
}
exports['default'] = {
init: init,
log: log
};
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _utils = __webpack_require__(6);
var _consts = __webpack_require__(5);
exports['default'] = function (flow) {
/**
* Return the immediate child nodes of the current node.
* **Getter only.**
*
* To create new child nodes, use the {@link flow.create} API.
* To reparent existing nodes, use the {@link flow.parent} API.
* > **Note:**
* > Note: this API only returns the immediate children of the current node.
* > To get all downstream nodes recursively, use the {@link flow.children.all} API.
* @see flow.parent
* @see flow.create
* @readonly
* @return {flow[]} children - Array of child nodes
*/
flow.children = function () {
(0, _utils.assert)(arguments.length, _consts.ERRORS.invalidChildren);
return flow.children.value.concat();
};
flow.children.value = [];
/**
* Check if the given node exists.
* @alias children.has
* @memberof flow
* @param {(String|Function|RegEx|flow)} matcher Matcher expression:
* ```
* // function:
* .has(node => node.data() === 5)
*
* // string:
* .has('foo')
*
* // regex
* .has(/$foo[a-Z]*^/)
*
* // flow
* .has(flowInstance)
* ```
* @param {Boolean} [recursive=true] recursive search or immediate children only.
* @return {Boolean} `true` if the matcher finds at least one node, else `false`.
*/
flow.children.has = function (matcher) {
var recursive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
return flow.children.find(matcher, recursive) !== undefined;
};
/**
* > **Aliases:**
* > - `children.find`
* @alias children.get
* @memberof flow
* @param {(String|Function|RegEx|flow)} matcher Matcher expression:
* ```
* // function:
* .children.get(node => node.data() === 5)
*
* // string:
* .children.get('foo')
*
* // regex
* .children.get(/$foo[a-Z]*^/)
*
* // flow
* .children.get(flowInstance)
* ```
* @param {Boolean} [recursive=true] recursive search or immediate children only.
* @return {flow|undefined} The first child node that matches the filter criteria, else `undefined`
*/
flow.children.find = function (matcher) {
var recursive = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
return flow.children.find.all(matcher, recursive).pop();
};
/**
* Find a child node based on a search criteria.
*
* > **Aliases:**
* > - `children.find.all`
* > - `children.findAll` (DEPRECATED)
* @alias children.get.all
* @memberof flow
* @param {(String|Function|RegEx|flow)} matcher Matcher expression:
* ```
* // function:
* .children.get.all(node => node.data() === 5)
*
* // string:
* .children.get.all('foo')
*
* // regex
* .children.get.all(/$foo[a-Z]*^/)
*
* // flow
* .children.get.all(flowInstance)
* ```
* @param {Boolean} [recursive=true] recursive search or immediate children only.
* @return {flow[]} All child nodes that match the filter criteria
*/
flow.children.find.all = function (matcher, recursive) {
var filter = (0, _utils.createMatcher)(matcher);
var children = recursive ? flow.children.all() : flow.children.value;
return children.filter(filter);
};
flow.children.findAll = flow.children.find.all;
flow.get = flow.children.find;
flow.get.all = flow.children.find.all;
flow.getAll = flow.children.findAll;
/**
* Return all child nodes recursively.
*
* @alias children.all
* @memberof flow
* @return {flow[]} All child nodes of the current node (recursive)
*/
flow.children.all = function () {
(0, _utils.assert)(arguments.length, _consts.ERRORS.invalidChildren);
var childMap = {};
return getChildren(flow);
function getChildren(flow) {
if (childMap[flow.guid()]) return [];
childMap[flow.guid()] = true;
var c = flow.children.value;
var gc = flow.children.value.map(getChildren);
return c.concat.apply(c, gc);
}
};
/**
* Get or set the the parent of the current node.
*
* **Reparenting:**
* ```
* let a = nflow.create('a')
* let b = nflow.create('b')
* a.parent(b) // reparent a onto b
* ```
*
* **Unparenting:**
* You can create a new standalone tree by setting the `parent` to `null`.
* ```
* let a = nflow.create('a')
* a.parent(null) // unparent `a` to form a new tree
* ```
*
* @param {(flow|null)} [parent] - the new parent node
* @returns {flow|null}
* (setter) The current flow node if a `parent` argument was given.
* (getter) The parent node of the current flow node if no arguments were given.
* (getter) `null` if no arguments were given and the current node has no parent.
* @emits 'flow.parent'
* @emits 'flow.parent.parent'
* @emits 'flow.children.parent'
* @emits 'flow.parented'
* @emits 'flow.parent.parented'
* @emits 'flow.children.parented'
*/
flow.parent = function () {
// TODO: accept an index parameter for attaching the flow node as the nth child
if (!arguments.length) return flow.parent.value;
var parent = arguments.length <= 0 ? undefined : arguments[0];
parent && (0, _utils.assert)(!(0, _utils.isFlow)(parent), _consts.ERRORS.invalidParent, parent);
var previousParent = flow.parent();
(0, _utils.detach)(flow);
/**
*
* Dispatched when a node is about to be reparented.
* @event 'flow.parent'
* @property {flow} flow - the node to be reparented.
* @property {flow} newParent - the the new parent node
* @property {flow} oldParent - the the old parent node
* @see flow.parent
*/
/**
*
* Dispatched when one of the node's parents is about to be reparented.
* @event 'flow.parent.parent'
* @property {flow} flow - the node to be reparented.
* @property {flow} newParent - the the new parent node
* @property {flow} oldParent - the the old parent node
* @see flow.parent
*/
/**
*
* Dispatched when ove of the node's children(recursive) is about to be reparented.
* @event 'flow.children.parent'
* @property {flow} flow - the node to be reparented.
* @property {flow} newParent - the the new parent node
* @property {flow} oldParent - the the old parent node
* @see flow.parent
*/
(0, _utils.dispatchInternalEvent)(flow, 'parent', parent, previousParent);
attach(parent);
/**
*
* Dispatched when a node has been reparented.
* @event 'flow.parented'
* @property {flow} flow - the reparented node.
* @property {flow} newParent - the the new parent node
* @property {flow} oldParent - the the old parent node
* @see flow.parent
*/
/**
*
* Dispatched when one of the node's parents has been reparented.
* @event 'flow.parent.parented'
* @property {flow} flow - the reparented node.
* @property {flow} newParent - the the new parent node
* @property {flow} oldParent - the the old parent node
* @see flow.parent
*/
/**
*
* Dispatched when ove of the node's children(recursive) has been reparented.
* @event 'flow.children.parented'
* @property {flow} flow - the reparented node.
* @property {flow} newParent - the the new parent node
* @property {flow} oldParent - the the old parent node
* @see flow.parent
*/
(0, _utils.dispatchInternalEvent)(flow, 'parented', parent, previousParent);
return flow;
};
/**
* Return an array of all parent nodes, starting from the elements parent, going upstream until a root node is found.
* @returns {flow[]} All parent nodes starting from the immediate parent to the root
*/
flow.parents = function () {
var parentMap = {};
parentMap[flow.guid.value] = true;
var parents = [];
var p = flow.parent.value;
while (p && !parentMap[p.guid.value]) {
parents.push(p);
parentMap[p.guid.value] = true;
p = p.parent.value;
}
return parents;
};
/**
* Find a parent node based on a search criteria.
*
* > **Aliases:**
* > - `parents.find`
* @alias parents.get
* @memberof flow
* @param {(String|Function|RegEx|flow)} matcher Matcher expression:
* ```
* // function:
* .parents.get(node => node.data() === 5)
*
* // string:
* .parents.get('foo')
*
* // regex
* .parents.get(/$foo[a-Z]*^/)
*
* // flow
* .parents.get(flowInstance)
* ```
* @return {flow|undefined} The nearest parent node that matches the criteria, else `undefined`
*/
flow.parents.find = function (matcher) {
if (matcher === null) return null;
var filter = matcher;
if (typeof matcher === 'string') filter = function filter(f) {
return f.name() === matcher;
};else if ((0, _utils.isFlow)(matcher)) filter = function filter(f) {
return f === matcher;
};
return flow.parents().filter(filter).pop();
};
flow.parents.get = flow.parents.find;
/**
* Check if the given parent node exists.
* @alias parents.has
* @memberof flow
* @param {(String|Function|RegEx|flow)} matcher Matcher expression:
* ```
* // function:
* .parents.has(node => node.data() === 5)
*
* // string:
* .parents.has('foo')
*
* // regex
* .parents.has(/$foo[a-Z]*^/)
*
* // flow
* .parents.has(flowInstance)
* ```
* @return {Boolean} `true` if the matcher finds at least one node, else `false`.
*/
flow.parents.has = function (matcher) {
return !!flow.parents.find(matcher);
};
/**
* Return the last node in the parent chain, ie. the node that has no further parents.
* @alias parents.root
* @memberof flow
* @return {flow|undefined} The root node if the current node has at least one parent, else `undefined`
*/
flow.parents.root = function () {
(0, _utils.assert)(arguments.length, _consts.ERRORS.invalidRoot);
return flow.parents().pop();
};
/**
* @internal
*/
flow.children.detach = function (child) {
flow.children.value = flow.children.value.filter(function (f) {
return f !== child;
});
(0, _utils.invalidateListenerCache)(flow);
};
flow.target = flow;
function attach(parent) {
flow.parent.value = parent;
if (parent) {
parent.children.value.push(flow);
(0, _utils.invalidateListenerCache)(parent);
}
}
};
/***/ },
/* 10 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _factory = __webpack_require__(2);
var _factory2 = _interopRequireDefault(_factory);
var _utils = __webpack_require__(6);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
exports['default'] = function (flow, defaults) {
/* jshint ignore:start */
/**
* Create a new flow instance.
* > **Note**: The parent of the newly created {@link flow} node is automatically set
* to the flow node it was created from.
* >
* > To create a new event tree that's not connected to existing nodes, simply unparent it after creation:
* > ```
* > let a = nflow
* > .create('new-tree')
* > .parent(null)
* > ```
* **Aliases**
* The following command chains have identical end results:
* - `.create('a', someData)`
* - `.create('a').data(someData)`
* - `.create().name('a').data(someData)`
*
* @memberof flow
* @param {string} name The name of the new node
* @param {...object} [data] optional data stored in the new node
* @returns {flow} the new flow instance
* @example
* let a = nflow.create('a')
* let b = nflow.create('b')
*
* let c = a.create('c')
* let d = a.create('d')
*
* @example <caption>second demo:</caption>
* let a = nflow.create('a')
* let b = nflow.create('b')
*
* @emits 'flow.create'
* @emits 'flow.parent.create'
* @emits 'flow.children.create'
*/
/* jshint ignore:end */
flow.create = function (name) {
for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
data[_key - 1] = arguments[_key];
}
var instance = (0, _factory2['default'])(flow.create.defaults, name, data);
instance.parent.value = flow;
flow.children.value.push(instance);
inheritStats(instance);
/**
*
* Dispatched when a node has been created.
* @event 'flow.create'
* @property {flow} flow - the node where the new node was created from(ie. the parent).
* @property {flow} newNode - the created node.
* @see flow.create
*/
/**
*
* Dispatched when one of the node's parents has been created.
* @event 'flow.parent.create'
* @property {flow} flow - the node where the new node was created from(ie. the parent).
* @property {flow} newNode - the created node.
* @see flow.create
*/
/**
*
* Dispatched when ove of the node's children(recursive) has been created.
* @event 'flow.children.create'
* @property {flow} flow - the node where the new node was created from(ie. the parent).
* @property {flow} newNode - the created node.
* @see flow.create
*/
(0, _utils.dispatchInternalEvent)(flow, 'create', instance);
return instance;
};
/**
* Create a new flow node or return the existing one if the current node already has a child with the same name.
* If multiple children have the same name, the first one will be returned.
* If `...data` parameters are given, it will also set the data on the newly created or existing node.
* @alias create.once
* @memberof flow
* @param {string} name The name of the new node
* @param {...object} [data] optional data stored in the node
* @returns {flow} the newly created or already existing flow instance
*/
flow.create.once = function (name) {
var _instance;
for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
data[_key2 - 1] = arguments[_key2];
}
var instance = flow.get(name);
if (instance) (_instance = instance).data.apply(_instance, data);else instance = flow.create.apply(flow, [name].concat(data));
return instance;
};
/**
* @internal
* @type {Object}
*/
flow.create.defaults = {
factory: defaults.factory,
behaviours: defaults.behaviours.concat(),
direction: defaults.direction
};
};
function inheritStats(flow) {
var p = flow.parent();
if (p) {
var defaults = p.stats.value.defaults || {};
var nodeDefaults = defaults[flow.name.value] || {};
/* jshint ignore:start */
flow.stats.value = _extends({}, nodeDefaults);
/* jshint ignore:end */
}
}
/***/ },
/* 11 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _consts = __webpack_require__(5);
var _utils = __webpack_require__(6);
var _routes = __webpack_require__(12);
var _routes2 = _interopRequireDefault(_routes);
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); } }
exports['default'] = function (flow) {
/**
* return the current status of the node
* @readonly
* @return {STATUS} The current status of the node
*/
flow.status = function () {
(0, _utils.assert)(arguments.length, _consts.ERRORS.invalidStatus);
if (flow.cancel.value) return _consts.STATUS.CANCELLED;
if (flow.dispose.value) return _consts.STATUS.DISPOSED;
return flow.status.value;
};
/**
* @internal
* @param {STATUS} The new status of the node
*/
flow.status.set = function (status) {
if (status === flow.status.value) return;
!flow.name.isInternal && (0, _utils.dispatchInternalEvent)(flow, 'status', status, flow.status.value, true);
flow.status.value = status;
};
flow.status.value = _consts.STATUS.IDLE;
(0, _utils.merge)(_consts.STATUS, flow.status);
/**
* Set the traversal direction of the node.
* The direction defines how the node traverses the event tree when it's emitted.
* @param {DIRECTION} [direction] The traversal direction
* @return {flow} The current flow node
*/
flow.direction = function () {
var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _consts.UNSET;
if (direction === _consts.UNSET) return flow.direction.value;
var oldDirection = flow.direction.value;
flow.direction.value = direction;
(0, _utils.dispatchInternalEvent)(flow, 'direction', direction, oldDirection);
return flow;
};
flow.direction.value = flow.create.defaults.direction;
(0, _utils.merge)(_consts.DIRECTION, flow.direction);
/**
* Emit a node to traverse the flow tree.
*
* In nflow `nodes` and `events` are the same type of objects.
* An event is a node that gets detached from the parent, traverses the tree (see {@tutorial propagation}) and gets delivered to all listeners (see {@tutorial namespacing}).
* > The `.emit` API has **3 distinct behaviours**:
* ```js
* foo.emit() // turns foo into an event and emits it
* foo.emit('bar') // creates bar and emits it on foo
* foo.emit(anotherNode) // reparents anotherNode to foo and emits it
* ```
* Essentially, the following two operations are the same:
* ```js
* foo.emit('bar')
* foo.create('bar').emit()
* ```
*
* #### Listener Context
* Listeners are always invoked in the context of the emitted event:
* ```js
* .on('price-update', function(d){
* this // refers to the emitted event
* this.data() // === d
* this.name() // === 'price-update'
* })
* ```
*
* Since **events are also flow objects**, you can dispatch further events on them! ({@tutorial event-chain})
* @param {String} [name] The name of the event
* @param {...object} [data] optional data stored on the event
* @returns {flow} the emitted event
* @tutorial event-chain
* @tutorial propagation
* @tutorial namespacing
* @emits 'flow.emit'
* @emits 'flow.parent.emit'
* @emits 'flow.children.emit'
* @emits 'flow.emitted'
* @emits 'flow.parent.emitted'
* @emits 'flow.children.emitted'
*/
flow.emit = function () {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _consts.UNSET;
return emit(name, args);
};
flow.emit.recipients = [];
flow.emit.recipientsMap = {};
createEmitAPI(flow);
/**
*
* Dispatched when a node is about to be emitted.
* @event 'flow.emit'
* @property {flow} parent - The emitter, ie. the parent of the emitted node.
* @property {flow} flow - the emitted node.
* @see flow.parent
*/
/**
*
* Dispatched when one of the node's parents is about to be emitted.
* @event 'flow.parent.emit'
* @property {flow} parent - The emitter, ie. the parent of the emitted node.
* @property {flow} flow - the emitted node.
* @see flow.emit
*/
/**
*
* Dispatched when ove of the node's children(recursive) is about to be emitted.
* @event 'flow.children.emit'
* @property {flow} parent - The emitter, ie. the parent of the emitted node.
* @property {flow} flow - the emitted node.
* @see flow.emit
*/
/**
*
* Dispatched after a node has been emitted.
* @event 'flow.emitted'
* @property {flow} parent - The emitter, ie. the parent of the emitted node.
* @property {flow} flow - the emitted node.
* @see flow.parent
*/
/**
*
* Dispatched after one of the node's parents has been emitted.
* @event 'flow.parent.emitted'
* @property {flow} parent - The emitter, ie. the parent of the emitted node.
* @property {flow} flow - the emitted node.
* @see flow.emit
*/
/**
*
* Dispatched after one of the node's children(recursive) has been emitted.
* @event 'flow.children.emitted'
* @property {flow} parent - The emitter, ie. the parent of the emitted node.
* @property {flow} flow - the emitted node.
* @see flow.emit
*/
function emit() {
var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _consts.UNSET;
var args = arguments[1];
var direction = arguments[2];
if (name === _consts.UNSET) {
// emit current flow object
(0, _utils.detach)(flow);
var p = flow.parent() || flow;
direction && flow.direction(direction);
(0, _utils.dispatchInternalEvent)(p, 'emit', flow, null, true);
p.emit.route(flow);
(0, _utils.dispatchInternalEvent)(p, 'emitted', flow, null, true);
return flow;
}
if ((0, _utils.isFlow)(name)) {
// 1. reparent the passed in flow object where it's emitted from
name.parent(flow);
// 2. emit the passed in flow object
(0, _utils.detach)(name);
direction && name.direction(direction);
args.length && name.data.apply(name, _toConsumableArray(args));
(0, _utils.dispatchInternalEvent)(flow, 'emit', name, null, true);
flow.emit.route(name);
(0, _utils.dispatchInternalEvent)(flow, 'emitted', name, null, true);
return flow;
}
// create and emit a new event
(0, _utils.assert)(typeof name !== 'string', _consts.ERRORS.invalidEventName);
var event = flow.create.apply(flow, [name].concat(_toConsumableArray(args)));
(0, _utils.detach)(event);
if (direction) event.direction.value = direction;
(0, _utils.dispatchInternalEvent)(flow, 'emit', event, null, true);
flow.emit.route(event);
(0, _utils.dispatchInternalEvent)(flow, 'emitted', event, null, true);
return event;
}
/**
* @internal
*/
flow.emit.route = function (event) {
event.stopPropagation.value = false;
event.status.set(_consts.STATUS.FLOWING);
var localName = (0, _utils.getLocalName)(event.name());
var matcher = function matcher(f) {
return f.on.listenerCache[localName];
};
event.emit.targets = flow.emit.route[event.direction()](event, matcher).concat(event.emit.targets || []);
while (event.emit.targets.length) {
var destination = event.emit.targets.shift();
if (event.isCancelled()) break;
if (event.propagationStopped()) break;
if (destination.flow.isCancelled()) continue;
notify(event, destination);
}
if (!event.isCancelled() && !event.propagationStopped()) {
event.status.set(_consts.STATUS.COMPLETED);
}
};
Object.keys(_routes2['default']).forEac