eventemitter3-graphology
Version:
A robust and multipurpose Graph object for JavaScript.
1,439 lines (1,327 loc) • 202 kB
JavaScript
(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