UNPKG

eventemitter3-graphology

Version:

A robust and multipurpose Graph object for JavaScript.

1,439 lines (1,327 loc) 202 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.graphology = factory()); })(this, (function () { 'use strict'; function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } function _construct(t, e, r) { if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments); var o = [null]; o.push.apply(o, e); var p = new (t.bind.apply(t, o))(); return r && _setPrototypeOf(p, r.prototype), p; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } function _inheritsLoose(t, o) { t.prototype = Object.create(o.prototype), t.prototype.constructor = t, _setPrototypeOf(t, o); } function _isNativeFunction(t) { try { return -1 !== Function.toString.call(t).indexOf("[native code]"); } catch (n) { return "function" == typeof t; } } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _wrapNativeSuper(t) { var r = "function" == typeof Map ? new Map() : void 0; return _wrapNativeSuper = function (t) { if (null === t || !_isNativeFunction(t)) return t; if ("function" != typeof t) throw new TypeError("Super expression must either be null or a function"); if (void 0 !== r) { if (r.has(t)) return r.get(t); r.set(t, Wrapper); } function Wrapper() { return _construct(t, arguments, _getPrototypeOf(this).constructor); } return Wrapper.prototype = Object.create(t.prototype, { constructor: { value: Wrapper, enumerable: !1, writable: !0, configurable: !0 } }), _setPrototypeOf(Wrapper, t); }, _wrapNativeSuper(t); } /** * Graphology Utilities * ===================== * * Collection of helpful functions used by the implementation. */ /** * Object.assign-like polyfill. * * @param {object} target - First object. * @param {object} [...objects] - Objects to merge. * @return {object} */ function assignPolyfill() { var target = arguments[0]; for (var i = 1, l = arguments.length; i < l; i++) { if (!arguments[i]) continue; for (var k in arguments[i]) target[k] = arguments[i][k]; } return target; } var assign = assignPolyfill; if (typeof Object.assign === 'function') assign = Object.assign; /** * Function returning the first matching edge for given path. * Note: this function does not check the existence of source & target. This * must be performed by the caller. * * @param {Graph} graph - Target graph. * @param {any} source - Source node. * @param {any} target - Target node. * @param {string} type - Type of the edge (mixed, directed or undirected). * @return {string|null} */ function getMatchingEdge(graph, source, target, type) { var sourceData = graph._nodes.get(source); var edge = null; if (!sourceData) return edge; if (type === 'mixed') { edge = sourceData.out && sourceData.out[target] || sourceData.undirected && sourceData.undirected[target]; } else if (type === 'directed') { edge = sourceData.out && sourceData.out[target]; } else { edge = sourceData.undirected && sourceData.undirected[target]; } return edge; } /** * Checks whether the given value is a plain object. * * @param {mixed} value - Target value. * @return {boolean} */ function isPlainObject(value) { // NOTE: as per https://github.com/graphology/graphology/issues/149 // this function has been loosened not to reject object instances // coming from other JavaScript contexts. It has also been chosen // not to improve it to avoid obvious false positives and avoid // taking a performance hit. People should really use TypeScript // if they want to avoid feeding subtly irrelvant attribute objects. return _typeof(value) === 'object' && value !== null; } /** * Checks whether the given object is empty. * * @param {object} o - Target Object. * @return {boolean} */ function isEmpty(o) { var k; for (k in o) return false; return true; } /** * Creates a "private" property for the given member name by concealing it * using the `enumerable` option. * * @param {object} target - Target object. * @param {string} name - Member name. */ function privateProperty(target, name, value) { Object.defineProperty(target, name, { enumerable: false, configurable: false, writable: true, value: value }); } /** * Creates a read-only property for the given member name & the given getter. * * @param {object} target - Target object. * @param {string} name - Member name. * @param {mixed} value - The attached getter or fixed value. */ function readOnlyProperty(target, name, value) { var descriptor = { enumerable: true, configurable: true }; if (typeof value === 'function') { descriptor.get = value; } else { descriptor.value = value; descriptor.writable = false; } Object.defineProperty(target, name, descriptor); } /** * Returns whether the given object constitute valid hints. * * @param {object} hints - Target object. */ function validateHints(hints) { if (!isPlainObject(hints)) return false; if (hints.attributes && !Array.isArray(hints.attributes)) return false; return true; } /** * Creates a function generating incremental ids for edges. * * @return {function} */ function incrementalIdStartingFromRandomByte() { var i = Math.floor(Math.random() * 256) & 0xff; return function () { return i++; }; } /** * Chains multiple iterators into a single iterator. * * @param {...Iterator} iterables * @returns {Iterator} */ function chain() { var iterables = arguments; var current = null; var i = -1; return _defineProperty(_defineProperty({}, Symbol.iterator, function () { return this; }), "next", function next() { var step = null; do { if (current === null) { i++; if (i >= iterables.length) return { done: true }; current = iterables[i][Symbol.iterator](); } step = current.next(); if (step.done) { current = null; continue; } break; // eslint-disable-next-line no-constant-condition } while (true); return step; }); } function emptyIterator() { return _defineProperty(_defineProperty({}, Symbol.iterator, function () { return this; }), "next", function next() { return { done: true }; }); } var eventemitter3 = {exports: {}}; (function (module) { var has = Object.prototype.hasOwnProperty , prefix = '~'; /** * Constructor to create a storage for our `EE` objects. * An `Events` instance is a plain object whose properties are event names. * * @constructor * @private */ function Events() {} // // We try to not inherit from `Object.prototype`. In some engines creating an // instance in this way is faster than calling `Object.create(null)` directly. // If `Object.create(null)` is not supported we prefix the event names with a // character to make sure that the built-in object properties are not // overridden or used as an attack vector. // if (Object.create) { Events.prototype = Object.create(null); // // This hack is needed because the `__proto__` property is still inherited in // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. // if (!new Events().__proto__) prefix = false; } /** * Representation of a single event listener. * * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} [once=false] Specify if the listener is a one-time listener. * @constructor * @private */ function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } /** * Add a listener for a given event. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} once Specify if the listener is a one-time listener. * @returns {EventEmitter} * @private */ function addListener(emitter, event, fn, context, once) { if (typeof fn !== 'function') { throw new TypeError('The listener must be a function'); } var listener = new EE(fn, context || emitter, once) , evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } /** * Clear event by name. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} evt The Event name. * @private */ function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events(); else delete emitter._events[evt]; } /** * Minimal `EventEmitter` interface that is molded against the Node.js * `EventEmitter` interface. * * @constructor * @public */ function EventEmitter() { this._events = new Events(); this._eventsCount = 0; } /** * Return an array listing the events for which the emitter has registered * listeners. * * @returns {Array} * @public */ EventEmitter.prototype.eventNames = function eventNames() { var names = [] , events , name; if (this._eventsCount === 0) return names; for (name in (events = this._events)) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; /** * Return the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Array} The registered listeners. * @public */ EventEmitter.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event , handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; /** * Return the number of listeners listening to a given event. * * @param {(String|Symbol)} event The event name. * @returns {Number} The number of listeners. * @public */ EventEmitter.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event , listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; /** * Calls each of the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Boolean} `true` if the event had listeners, else `false`. * @public */ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt] , len = arguments.length , args , i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len -1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length , j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; /** * Add a listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; /** * Add a one-time listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; /** * Remove the listeners of a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn Only remove the listeners that match this function. * @param {*} context Only remove the listeners that have this context. * @param {Boolean} once Only remove one-time listeners. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if ( listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context) ) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if ( listeners[i].fn !== fn || (once && !listeners[i].once) || (context && listeners[i].context !== context) ) { events.push(listeners[i]); } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; /** * Remove all listeners, or those of the specified event. * * @param {(String|Symbol)} [event] The event name. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; // // Alias methods names because people roll like that. // EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.addListener = EventEmitter.prototype.on; // // Expose the prefix. // EventEmitter.prefixed = prefix; // // Allow `EventEmitter` to be imported as module namespace. // EventEmitter.EventEmitter = EventEmitter; // // Expose the module. // { module.exports = EventEmitter; } }(eventemitter3)); var EventEmitter = eventemitter3.exports; /** * Graphology Custom Errors * ========================= * * Defining custom errors for ease of use & easy unit tests across * implementations (normalized typology rather than relying on error * messages to check whether the correct error was found). */ var GraphError = /*#__PURE__*/function (_Error) { _inheritsLoose(GraphError, _Error); function GraphError(message) { var _this; _this = _Error.call(this) || this; _this.name = 'GraphError'; _this.message = message; return _this; } return GraphError; }( /*#__PURE__*/_wrapNativeSuper(Error)); var InvalidArgumentsGraphError = /*#__PURE__*/function (_GraphError) { _inheritsLoose(InvalidArgumentsGraphError, _GraphError); function InvalidArgumentsGraphError(message) { var _this2; _this2 = _GraphError.call(this, message) || this; _this2.name = 'InvalidArgumentsGraphError'; // This is V8 specific to enhance stack readability if (typeof Error.captureStackTrace === 'function') Error.captureStackTrace(_assertThisInitialized(_this2), InvalidArgumentsGraphError.prototype.constructor); return _this2; } return InvalidArgumentsGraphError; }(GraphError); var NotFoundGraphError = /*#__PURE__*/function (_GraphError2) { _inheritsLoose(NotFoundGraphError, _GraphError2); function NotFoundGraphError(message) { var _this3; _this3 = _GraphError2.call(this, message) || this; _this3.name = 'NotFoundGraphError'; // This is V8 specific to enhance stack readability if (typeof Error.captureStackTrace === 'function') Error.captureStackTrace(_assertThisInitialized(_this3), NotFoundGraphError.prototype.constructor); return _this3; } return NotFoundGraphError; }(GraphError); var UsageGraphError = /*#__PURE__*/function (_GraphError3) { _inheritsLoose(UsageGraphError, _GraphError3); function UsageGraphError(message) { var _this4; _this4 = _GraphError3.call(this, message) || this; _this4.name = 'UsageGraphError'; // This is V8 specific to enhance stack readability if (typeof Error.captureStackTrace === 'function') Error.captureStackTrace(_assertThisInitialized(_this4), UsageGraphError.prototype.constructor); return _this4; } return UsageGraphError; }(GraphError); /** * Graphology Internal Data Classes * ================================= * * Internal classes hopefully reduced to structs by engines & storing * necessary information for nodes & edges. * * Note that those classes don't rely on the `class` keyword to avoid some * cruft introduced by most of ES2015 transpilers. */ /** * MixedNodeData class. * * @constructor * @param {string} string - The node's key. * @param {object} attributes - Node's attributes. */ function MixedNodeData(key, attributes) { // Attributes this.key = key; this.attributes = attributes; this.clear(); } MixedNodeData.prototype.clear = function () { // Degrees this.inDegree = 0; this.outDegree = 0; this.undirectedDegree = 0; this.undirectedLoops = 0; this.directedLoops = 0; // Indices this["in"] = {}; this.out = {}; this.undirected = {}; }; /** * DirectedNodeData class. * * @constructor * @param {string} string - The node's key. * @param {object} attributes - Node's attributes. */ function DirectedNodeData(key, attributes) { // Attributes this.key = key; this.attributes = attributes; this.clear(); } DirectedNodeData.prototype.clear = function () { // Degrees this.inDegree = 0; this.outDegree = 0; this.directedLoops = 0; // Indices this["in"] = {}; this.out = {}; }; /** * UndirectedNodeData class. * * @constructor * @param {string} string - The node's key. * @param {object} attributes - Node's attributes. */ function UndirectedNodeData(key, attributes) { // Attributes this.key = key; this.attributes = attributes; this.clear(); } UndirectedNodeData.prototype.clear = function () { // Degrees this.undirectedDegree = 0; this.undirectedLoops = 0; // Indices this.undirected = {}; }; /** * EdgeData class. * * @constructor * @param {boolean} undirected - Whether the edge is undirected. * @param {string} string - The edge's key. * @param {string} source - Source of the edge. * @param {string} target - Target of the edge. * @param {object} attributes - Edge's attributes. */ function EdgeData(undirected, key, source, target, attributes) { // Attributes this.key = key; this.attributes = attributes; this.undirected = undirected; // Extremities this.source = source; this.target = target; } EdgeData.prototype.attach = function () { var outKey = 'out'; var inKey = 'in'; if (this.undirected) outKey = inKey = 'undirected'; var source = this.source.key; var target = this.target.key; // Handling source this.source[outKey][target] = this; if (this.undirected && source === target) return; // Handling target this.target[inKey][source] = this; }; EdgeData.prototype.attachMulti = function () { var outKey = 'out'; var inKey = 'in'; var source = this.source.key; var target = this.target.key; if (this.undirected) outKey = inKey = 'undirected'; // Handling source var adj = this.source[outKey]; var head = adj[target]; if (typeof head === 'undefined') { adj[target] = this; // Self-loop optimization if (!(this.undirected && source === target)) { // Handling target this.target[inKey][source] = this; } return; } // Prepending to doubly-linked list head.previous = this; this.next = head; // Pointing to new head // NOTE: use mutating swap later to avoid lookup? adj[target] = this; this.target[inKey][source] = this; }; EdgeData.prototype.detach = function () { var source = this.source.key; var target = this.target.key; var outKey = 'out'; var inKey = 'in'; if (this.undirected) outKey = inKey = 'undirected'; delete this.source[outKey][target]; // No-op delete in case of undirected self-loop delete this.target[inKey][source]; }; EdgeData.prototype.detachMulti = function () { var source = this.source.key; var target = this.target.key; var outKey = 'out'; var inKey = 'in'; if (this.undirected) outKey = inKey = 'undirected'; // Deleting from doubly-linked list if (this.previous === undefined) { // We are dealing with the head // Should we delete the adjacency entry because it is now empty? if (this.next === undefined) { delete this.source[outKey][target]; // No-op delete in case of undirected self-loop delete this.target[inKey][source]; } else { // Detaching this.next.previous = undefined; // NOTE: could avoid the lookups by creating a #.become mutating method this.source[outKey][target] = this.next; // No-op delete in case of undirected self-loop this.target[inKey][source] = this.next; } } else { // We are dealing with another list node this.previous.next = this.next; // If not last if (this.next !== undefined) { this.next.previous = this.previous; } } }; /** * Graphology Node Attributes methods * =================================== */ var NODE = 0; var SOURCE = 1; var TARGET = 2; var OPPOSITE = 3; function findRelevantNodeData(graph, method, mode, nodeOrEdge, nameOrEdge, add1, add2) { var nodeData, edgeData, arg1, arg2; nodeOrEdge = '' + nodeOrEdge; if (mode === NODE) { nodeData = graph._nodes.get(nodeOrEdge); if (!nodeData) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(nodeOrEdge, "\" node in the graph.")); arg1 = nameOrEdge; arg2 = add1; } else if (mode === OPPOSITE) { nameOrEdge = '' + nameOrEdge; edgeData = graph._edges.get(nameOrEdge); if (!edgeData) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(nameOrEdge, "\" edge in the graph.")); var source = edgeData.source.key; var target = edgeData.target.key; if (nodeOrEdge === source) { nodeData = edgeData.target; } else if (nodeOrEdge === target) { nodeData = edgeData.source; } else { throw new NotFoundGraphError("Graph.".concat(method, ": the \"").concat(nodeOrEdge, "\" node is not attached to the \"").concat(nameOrEdge, "\" edge (").concat(source, ", ").concat(target, ").")); } arg1 = add1; arg2 = add2; } else { edgeData = graph._edges.get(nodeOrEdge); if (!edgeData) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(nodeOrEdge, "\" edge in the graph.")); if (mode === SOURCE) { nodeData = edgeData.source; } else { nodeData = edgeData.target; } arg1 = nameOrEdge; arg2 = add1; } return [nodeData, arg1, arg2]; } function attachNodeAttributeGetter(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge, add1) { var _findRelevantNodeData = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1), data = _findRelevantNodeData[0], name = _findRelevantNodeData[1]; return data.attributes[name]; }; } function attachNodeAttributesGetter(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge) { var _findRelevantNodeData2 = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge), data = _findRelevantNodeData2[0]; return data.attributes; }; } function attachNodeAttributeChecker(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge, add1) { var _findRelevantNodeData3 = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1), data = _findRelevantNodeData3[0], name = _findRelevantNodeData3[1]; return data.attributes.hasOwnProperty(name); }; } function attachNodeAttributeSetter(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge, add1, add2) { var _findRelevantNodeData4 = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1, add2), data = _findRelevantNodeData4[0], name = _findRelevantNodeData4[1], value = _findRelevantNodeData4[2]; data.attributes[name] = value; // Emitting this.emit('nodeAttributesUpdated', { key: data.key, type: 'set', attributes: data.attributes, name: name }); return this; }; } function attachNodeAttributeUpdater(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge, add1, add2) { var _findRelevantNodeData5 = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1, add2), data = _findRelevantNodeData5[0], name = _findRelevantNodeData5[1], updater = _findRelevantNodeData5[2]; if (typeof updater !== 'function') throw new InvalidArgumentsGraphError("Graph.".concat(method, ": updater should be a function.")); var attributes = data.attributes; var value = updater(attributes[name]); attributes[name] = value; // Emitting this.emit('nodeAttributesUpdated', { key: data.key, type: 'set', attributes: data.attributes, name: name }); return this; }; } function attachNodeAttributeRemover(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge, add1) { var _findRelevantNodeData6 = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1), data = _findRelevantNodeData6[0], name = _findRelevantNodeData6[1]; delete data.attributes[name]; // Emitting this.emit('nodeAttributesUpdated', { key: data.key, type: 'remove', attributes: data.attributes, name: name }); return this; }; } function attachNodeAttributesReplacer(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge, add1) { var _findRelevantNodeData7 = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1), data = _findRelevantNodeData7[0], attributes = _findRelevantNodeData7[1]; if (!isPlainObject(attributes)) throw new InvalidArgumentsGraphError("Graph.".concat(method, ": provided attributes are not a plain object.")); data.attributes = attributes; // Emitting this.emit('nodeAttributesUpdated', { key: data.key, type: 'replace', attributes: data.attributes }); return this; }; } function attachNodeAttributesMerger(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge, add1) { var _findRelevantNodeData8 = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1), data = _findRelevantNodeData8[0], attributes = _findRelevantNodeData8[1]; if (!isPlainObject(attributes)) throw new InvalidArgumentsGraphError("Graph.".concat(method, ": provided attributes are not a plain object.")); assign(data.attributes, attributes); // Emitting this.emit('nodeAttributesUpdated', { key: data.key, type: 'merge', attributes: data.attributes, data: attributes }); return this; }; } function attachNodeAttributesUpdater(Class, method, mode) { Class.prototype[method] = function (nodeOrEdge, nameOrEdge, add1) { var _findRelevantNodeData9 = findRelevantNodeData(this, method, mode, nodeOrEdge, nameOrEdge, add1), data = _findRelevantNodeData9[0], updater = _findRelevantNodeData9[1]; if (typeof updater !== 'function') throw new InvalidArgumentsGraphError("Graph.".concat(method, ": provided updater is not a function.")); data.attributes = updater(data.attributes); // Emitting this.emit('nodeAttributesUpdated', { key: data.key, type: 'update', attributes: data.attributes }); return this; }; } /** * List of methods to attach. */ var NODE_ATTRIBUTES_METHODS = [{ name: function name(element) { return "get".concat(element, "Attribute"); }, attacher: attachNodeAttributeGetter }, { name: function name(element) { return "get".concat(element, "Attributes"); }, attacher: attachNodeAttributesGetter }, { name: function name(element) { return "has".concat(element, "Attribute"); }, attacher: attachNodeAttributeChecker }, { name: function name(element) { return "set".concat(element, "Attribute"); }, attacher: attachNodeAttributeSetter }, { name: function name(element) { return "update".concat(element, "Attribute"); }, attacher: attachNodeAttributeUpdater }, { name: function name(element) { return "remove".concat(element, "Attribute"); }, attacher: attachNodeAttributeRemover }, { name: function name(element) { return "replace".concat(element, "Attributes"); }, attacher: attachNodeAttributesReplacer }, { name: function name(element) { return "merge".concat(element, "Attributes"); }, attacher: attachNodeAttributesMerger }, { name: function name(element) { return "update".concat(element, "Attributes"); }, attacher: attachNodeAttributesUpdater }]; /** * Attach every attributes-related methods to a Graph class. * * @param {function} Graph - Target class. */ function attachNodeAttributesMethods(Graph) { NODE_ATTRIBUTES_METHODS.forEach(function (_ref) { var name = _ref.name, attacher = _ref.attacher; // For nodes attacher(Graph, name('Node'), NODE); // For sources attacher(Graph, name('Source'), SOURCE); // For targets attacher(Graph, name('Target'), TARGET); // For opposites attacher(Graph, name('Opposite'), OPPOSITE); }); } /** * Graphology Edge Attributes methods * =================================== */ /** * Attach an attribute getter method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeGetter(Class, method, type) { /** * Get the desired attribute for the given element (node or edge). * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * * @return {mixed} - The attribute's value. * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function (element, name) { var data; if (this.type !== 'mixed' && type !== 'mixed' && type !== this.type) throw new UsageGraphError("Graph.".concat(method, ": cannot find this type of edges in your ").concat(this.type, " graph.")); if (arguments.length > 2) { if (this.multi) throw new UsageGraphError("Graph.".concat(method, ": cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.")); var source = '' + element; var target = '' + name; name = arguments[2]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find an edge for the given path (\"").concat(source, "\" - \"").concat(target, "\").")); } else { if (type !== 'mixed') throw new UsageGraphError("Graph.".concat(method, ": calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.")); element = '' + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(element, "\" edge in the graph.")); } return data.attributes[name]; }; } /** * Attach an attributes getter method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributesGetter(Class, method, type) { /** * Retrieves all the target element's attributes. * * Arity 2: * @param {any} element - Target element. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * * @return {object} - The element's attributes. * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function (element) { var data; if (this.type !== 'mixed' && type !== 'mixed' && type !== this.type) throw new UsageGraphError("Graph.".concat(method, ": cannot find this type of edges in your ").concat(this.type, " graph.")); if (arguments.length > 1) { if (this.multi) throw new UsageGraphError("Graph.".concat(method, ": cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.")); var source = '' + element, target = '' + arguments[1]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find an edge for the given path (\"").concat(source, "\" - \"").concat(target, "\").")); } else { if (type !== 'mixed') throw new UsageGraphError("Graph.".concat(method, ": calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.")); element = '' + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(element, "\" edge in the graph.")); } return data.attributes; }; } /** * Attach an attribute checker method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeChecker(Class, method, type) { /** * Checks whether the desired attribute is set for the given element (node or edge). * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * * @return {boolean} * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function (element, name) { var data; if (this.type !== 'mixed' && type !== 'mixed' && type !== this.type) throw new UsageGraphError("Graph.".concat(method, ": cannot find this type of edges in your ").concat(this.type, " graph.")); if (arguments.length > 2) { if (this.multi) throw new UsageGraphError("Graph.".concat(method, ": cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.")); var source = '' + element; var target = '' + name; name = arguments[2]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find an edge for the given path (\"").concat(source, "\" - \"").concat(target, "\").")); } else { if (type !== 'mixed') throw new UsageGraphError("Graph.".concat(method, ": calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.")); element = '' + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(element, "\" edge in the graph.")); } return data.attributes.hasOwnProperty(name); }; } /** * Attach an attribute setter method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeSetter(Class, method, type) { /** * Set the desired attribute for the given element (node or edge). * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * @param {mixed} value - New attribute value. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * @param {mixed} value - New attribute value. * * @return {Graph} - Returns itself for chaining. * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function (element, name, value) { var data; if (this.type !== 'mixed' && type !== 'mixed' && type !== this.type) throw new UsageGraphError("Graph.".concat(method, ": cannot find this type of edges in your ").concat(this.type, " graph.")); if (arguments.length > 3) { if (this.multi) throw new UsageGraphError("Graph.".concat(method, ": cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.")); var source = '' + element; var target = '' + name; name = arguments[2]; value = arguments[3]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find an edge for the given path (\"").concat(source, "\" - \"").concat(target, "\").")); } else { if (type !== 'mixed') throw new UsageGraphError("Graph.".concat(method, ": calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.")); element = '' + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(element, "\" edge in the graph.")); } data.attributes[name] = value; // Emitting this.emit('edgeAttributesUpdated', { key: data.key, type: 'set', attributes: data.attributes, name: name }); return this; }; } /** * Attach an attribute updater method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeUpdater(Class, method, type) { /** * Update the desired attribute for the given element (node or edge) using * the provided function. * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * @param {function} updater - Updater function. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * @param {function} updater - Updater function. * * @return {Graph} - Returns itself for chaining. * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function (element, name, updater) { var data; if (this.type !== 'mixed' && type !== 'mixed' && type !== this.type) throw new UsageGraphError("Graph.".concat(method, ": cannot find this type of edges in your ").concat(this.type, " graph.")); if (arguments.length > 3) { if (this.multi) throw new UsageGraphError("Graph.".concat(method, ": cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.")); var source = '' + element; var target = '' + name; name = arguments[2]; updater = arguments[3]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find an edge for the given path (\"").concat(source, "\" - \"").concat(target, "\").")); } else { if (type !== 'mixed') throw new UsageGraphError("Graph.".concat(method, ": calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.")); element = '' + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(element, "\" edge in the graph.")); } if (typeof updater !== 'function') throw new InvalidArgumentsGraphError("Graph.".concat(method, ": updater should be a function.")); data.attributes[name] = updater(data.attributes[name]); // Emitting this.emit('edgeAttributesUpdated', { key: data.key, type: 'set', attributes: data.attributes, name: name }); return this; }; } /** * Attach an attribute remover method onto the provided class. * * @param {function} Class - Target class. * @param {string} method - Method name. * @param {string} type - Type of the edge to find. */ function attachEdgeAttributeRemover(Class, method, type) { /** * Remove the desired attribute for the given element (node or edge). * * Arity 2: * @param {any} element - Target element. * @param {string} name - Attribute's name. * * Arity 3 (only for edges): * @param {any} source - Source element. * @param {any} target - Target element. * @param {string} name - Attribute's name. * * @return {Graph} - Returns itself for chaining. * * @throws {Error} - Will throw if too many arguments are provided. * @throws {Error} - Will throw if any of the elements is not found. */ Class.prototype[method] = function (element, name) { var data; if (this.type !== 'mixed' && type !== 'mixed' && type !== this.type) throw new UsageGraphError("Graph.".concat(method, ": cannot find this type of edges in your ").concat(this.type, " graph.")); if (arguments.length > 2) { if (this.multi) throw new UsageGraphError("Graph.".concat(method, ": cannot use a {source,target} combo when asking about an edge's attributes in a MultiGraph since we cannot infer the one you want information about.")); var source = '' + element; var target = '' + name; name = arguments[2]; data = getMatchingEdge(this, source, target, type); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find an edge for the given path (\"").concat(source, "\" - \"").concat(target, "\").")); } else { if (type !== 'mixed') throw new UsageGraphError("Graph.".concat(method, ": calling this method with only a key (vs. a source and target) does not make sense since an edge with this key could have the other type.")); element = '' + element; data = this._edges.get(element); if (!data) throw new NotFoundGraphError("Graph.".concat(method, ": could not find the \"").concat(element, "\" edge in the graph.")); } delete data.attributes[name]; // Emitting this.emit('edgeAttributesUpdated', { key: data.key, type: 'remove', attributes: data.attributes, name: name }); return this; }; } /** * Attach an attribute replacer method onto the provided class. * * @param {function} Class - Target class. * @param