(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ngraphCreate2dLayout = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o max_x) max_x = bodyPos.x; if (bodyPos.y > max_y) max_y = bodyPos.y; } boundingBox.min_x = min_x; boundingBox.min_y = min_y; boundingBox.max_x = max_x; boundingBox.max_y = max_y; } function resetBoundingBox() { boundingBox.min_x = boundingBox.max_x = 0; boundingBox.min_y = boundingBox.max_y = 0; } } } },{}],3:[function(require,module,exports){ function Vector(x, y) { if (typeof arguments[0] === 'object') { // could be another vector let v = arguments[0]; if (!Number.isFinite(v.x)) throw new Error("Expected value is not a finite number at Vector constructor (x)"); if (!Number.isFinite(v.y)) throw new Error("Expected value is not a finite number at Vector constructor (y)"); this.x = v.x; this.y = v.y; } else { this.x = typeof x === "number" ? x : 0; this.y = typeof y === "number" ? y : 0; } } Vector.prototype.reset = function () { this.x = this.y = 0; }; function Body(x, y) { this.isPinned = false; this.pos = new Vector(x, y); this.force = new Vector(); this.velocity = new Vector(); this.mass = 1; this.springCount = 0; this.springLength = 0; } Body.prototype.reset = function() { this.force.reset(); this.springCount = 0; this.springLength = 0; } Body.prototype.setPosition = function (x, y) { this.pos.x = x || 0; this.pos.y = y || 0; }; module.exports = function() { return Body; } },{}],4:[function(require,module,exports){ module.exports = function() { return function anonymous(options ) { if (!Number.isFinite(options.dragCoefficient)) throw new Error('dragCoefficient is not a finite number'); return { update: function(body) { body.force.x -= options.dragCoefficient * body.velocity.x; body.force.y -= options.dragCoefficient * body.velocity.y; } }; } } },{}],5:[function(require,module,exports){ module.exports = function() { return function anonymous(options,random ) { if (!Number.isFinite(options.springCoefficient)) throw new Error('Spring coefficient is not a number'); if (!Number.isFinite(options.springLength)) throw new Error('Spring length is not a number'); return { /** * Updates forces acting on a spring */ update: function (spring) { var body1 = spring.from; var body2 = spring.to; var length = spring.length < 0 ? options.springLength : spring.length; var dx = body2.pos.x - body1.pos.x; var dy = body2.pos.y - body1.pos.y; var r = Math.sqrt(dx * dx + dy * dy); if (r === 0) { dx = (random.nextDouble() - 0.5) / 50; dy = (random.nextDouble() - 0.5) / 50; r = Math.sqrt(dx * dx + dy * dy); } var d = r - length; var coefficient = ((spring.coefficient > 0) ? spring.coefficient : options.springCoefficient) * d / r; body1.force.x += coefficient * dx body1.force.y += coefficient * dy; body1.springCount += 1; body1.springLength += r; body2.force.x -= coefficient * dx body2.force.y -= coefficient * dy; body2.springCount += 1; body2.springLength += r; } }; } } },{}],6:[function(require,module,exports){ module.exports = function() { return function anonymous(bodies,timeStep,adaptiveTimeStepWeight ) { var length = bodies.length; if (length === 0) return 0; var dx = 0, tx = 0; var dy = 0, ty = 0; for (var i = 0; i < length; ++i) { var body = bodies[i]; if (body.isPinned) continue; if (adaptiveTimeStepWeight && body.springCount) { timeStep = (adaptiveTimeStepWeight * body.springLength/body.springCount); } var coeff = timeStep / body.mass; body.velocity.x += coeff * body.force.x; body.velocity.y += coeff * body.force.y; var vx = body.velocity.x; var vy = body.velocity.y; var v = Math.sqrt(vx * vx + vy * vy); if (v > 1) { // We normalize it so that we move within timeStep range. // for the case when v <= 1 - we let velocity to fade out. body.velocity.x = vx / v; body.velocity.y = vy / v; } dx = timeStep * body.velocity.x; dy = timeStep * body.velocity.y; body.pos.x += dx; body.pos.y += dy; tx += Math.abs(dx); ty += Math.abs(dy); } return (tx * tx + ty * ty)/length; } } },{}],7:[function(require,module,exports){ /** * Our implementation of QuadTree is non-recursive to avoid GC hit * This data structure represent stack of elements * which we are trying to insert into quad tree. */ function InsertStack () { this.stack = []; this.popIdx = 0; } InsertStack.prototype = { isEmpty: function() { return this.popIdx === 0; }, push: function (node, body) { var item = this.stack[this.popIdx]; if (!item) { // we are trying to avoid memory pressure: create new element // only when absolutely necessary this.stack[this.popIdx] = new InsertStackElement(node, body); } else { item.node = node; item.body = body; } ++this.popIdx; }, pop: function () { if (this.popIdx > 0) { return this.stack[--this.popIdx]; } }, reset: function () { this.popIdx = 0; } }; function InsertStackElement(node, body) { this.node = node; // QuadTree node this.body = body; // physical body which needs to be inserted to node } function QuadNode() { // body stored inside this node. In quad tree only leaf nodes (by construction) // contain bodies: this.body = null; // Child nodes are stored in quads. Each quad is presented by number: // 0 | 1 // ----- // 2 | 3 this.quad0 = null; this.quad1 = null; this.quad2 = null; this.quad3 = null; // Total mass of current node this.mass = 0; // Center of mass coordinates this.mass_x = 0; this.mass_y = 0; // bounding box coordinates this.min_x = 0; this.min_y = 0; this.max_x = 0; this.max_y = 0; } function isSamePosition(point1, point2) { var dx = Math.abs(point1.x - point2.x); var dy = Math.abs(point1.y - point2.y); return dx < 1e-8 && dy < 1e-8; } function getChild(node, idx) { if (idx === 0) return node.quad0; if (idx === 1) return node.quad1; if (idx === 2) return node.quad2; if (idx === 3) return node.quad3; return null; } function setChild(node, idx, child) { if (idx === 0) node.quad0 = child; else if (idx === 1) node.quad1 = child; else if (idx === 2) node.quad2 = child; else if (idx === 3) node.quad3 = child; } module.exports = function() { return function createQuadTree(options, random) { options = options || {}; options.gravity = typeof options.gravity === 'number' ? options.gravity : -1; options.theta = typeof options.theta === 'number' ? options.theta : 0.8; var gravity = options.gravity; var updateQueue = []; var insertStack = new InsertStack(); var theta = options.theta; var nodesCache = []; var currentInCache = 0; var root = newNode(); return { insertBodies: insertBodies, /** * Gets root node if it is present */ getRoot: function() { return root; }, updateBodyForce: update, options: function(newOptions) { if (newOptions) { if (typeof newOptions.gravity === 'number') { gravity = newOptions.gravity; } if (typeof newOptions.theta === 'number') { theta = newOptions.theta; } return this; } return { gravity: gravity, theta: theta }; } }; function newNode() { // To avoid pressure on GC we reuse nodes. var node = nodesCache[currentInCache]; if (node) { node.quad0 = null; node.quad1 = null; node.quad2 = null; node.quad3 = null; node.body = null; node.mass = node.mass_x = node.mass_y = 0; node.min_x = node.max_x = node.min_y = node.max_y = 0; } else { node = new QuadNode(); nodesCache[currentInCache] = node; } ++currentInCache; return node; } function update(sourceBody) { var queue = updateQueue; var v; var dx; var dy; var r; var fx = 0; var fy = 0; var queueLength = 1; var shiftIdx = 0; var pushIdx = 1; queue[0] = root; while (queueLength) { var node = queue[shiftIdx]; var body = node.body; queueLength -= 1; shiftIdx += 1; var differentBody = (body !== sourceBody); if (body && differentBody) { // If the current node is a leaf node (and it is not source body), // calculate the force exerted by the current node on body, and add this // amount to body's net force. dx = body.pos.x - sourceBody.pos.x; dy = body.pos.y - sourceBody.pos.y; r = Math.sqrt(dx * dx + dy * dy); if (r === 0) { // Poor man's protection against zero distance. dx = (random.nextDouble() - 0.5) / 50; dy = (random.nextDouble() - 0.5) / 50; r = Math.sqrt(dx * dx + dy * dy); } // This is standard gravitation force calculation but we divide // by r^3 to save two operations when normalizing force vector. v = gravity * body.mass * sourceBody.mass / (r * r * r); fx += v * dx; fy += v * dy; } else if (differentBody) { // Otherwise, calculate the ratio s / r, where s is the width of the region // represented by the internal node, and r is the distance between the body // and the node's center-of-mass dx = node.mass_x / node.mass - sourceBody.pos.x; dy = node.mass_y / node.mass - sourceBody.pos.y; r = Math.sqrt(dx * dx + dy * dy); if (r === 0) { // Sorry about code duplication. I don't want to create many functions // right away. Just want to see performance first. dx = (random.nextDouble() - 0.5) / 50; dy = (random.nextDouble() - 0.5) / 50; r = Math.sqrt(dx * dx + dy * dy); } // If s / r < θ, treat this internal node as a single body, and calculate the // force it exerts on sourceBody, and add this amount to sourceBody's net force. if ((node.max_x - node.min_x) / r < theta) { // in the if statement above we consider node's width only // because the region was made into square during tree creation. // Thus there is no difference between using width or height. v = gravity * node.mass * sourceBody.mass / (r * r * r); fx += v * dx; fy += v * dy; } else { // Otherwise, run the procedure recursively on each of the current node's children. // I intentionally unfolded this loop, to save several CPU cycles. if (node.quad0) { queue[pushIdx] = node.quad0; queueLength += 1; pushIdx += 1; } if (node.quad1) { queue[pushIdx] = node.quad1; queueLength += 1; pushIdx += 1; } if (node.quad2) { queue[pushIdx] = node.quad2; queueLength += 1; pushIdx += 1; } if (node.quad3) { queue[pushIdx] = node.quad3; queueLength += 1; pushIdx += 1; } } } } sourceBody.force.x += fx; sourceBody.force.y += fy; } function insertBodies(bodies) { var xmin = Number.MAX_VALUE; var ymin = Number.MAX_VALUE; var xmax = Number.MIN_VALUE; var ymax = Number.MIN_VALUE; var i = bodies.length; // To reduce quad tree depth we are looking for exact bounding box of all particles. while (i--) { var pos = bodies[i].pos; if (pos.x < xmin) xmin = pos.x; if (pos.y < ymin) ymin = pos.y; if (pos.x > xmax) xmax = pos.x; if (pos.y > ymax) ymax = pos.y; } // Makes the bounds square. var maxSideLength = -Infinity; if (xmax - xmin > maxSideLength) maxSideLength = xmax - xmin ; if (ymax - ymin > maxSideLength) maxSideLength = ymax - ymin ; currentInCache = 0; root = newNode(); root.min_x = xmin; root.min_y = ymin; root.max_x = xmin + maxSideLength; root.max_y = ymin + maxSideLength; i = bodies.length - 1; if (i >= 0) { root.body = bodies[i]; } while (i--) { insert(bodies[i], root); } } function insert(newBody) { insertStack.reset(); insertStack.push(root, newBody); while (!insertStack.isEmpty()) { var stackItem = insertStack.pop(); var node = stackItem.node; var body = stackItem.body; if (!node.body) { // This is internal node. Update the total mass of the node and center-of-mass. var x = body.pos.x; var y = body.pos.y; node.mass += body.mass; node.mass_x += body.mass * x; node.mass_y += body.mass * y; // Recursively insert the body in the appropriate quadrant. // But first find the appropriate quadrant. var quadIdx = 0; // Assume we are in the 0's quad. var min_x = node.min_x; var min_y = node.min_y; var max_x = (min_x + node.max_x) / 2; var max_y = (min_y + node.max_y) / 2; if (x > max_x) { quadIdx = quadIdx + 1; min_x = max_x; max_x = node.max_x; } if (y > max_y) { quadIdx = quadIdx + 2; min_y = max_y; max_y = node.max_y; } var child = getChild(node, quadIdx); if (!child) { // The node is internal but this quadrant is not taken. Add // subnode to it. child = newNode(); child.min_x = min_x; child.min_y = min_y; child.max_x = max_x; child.max_y = max_y; child.body = body; setChild(node, quadIdx, child); } else { // continue searching in this quadrant. insertStack.push(child, body); } } else { // We are trying to add to the leaf node. // We have to convert current leaf into internal node // and continue adding two nodes. var oldBody = node.body; node.body = null; // internal nodes do not cary bodies if (isSamePosition(oldBody.pos, body.pos)) { // Prevent infinite subdivision by bumping one node // anywhere in this quadrant var retriesCount = 3; do { var offset = random.nextDouble(); var dx = (node.max_x - node.min_x) * offset; var dy = (node.max_y - node.min_y) * offset; oldBody.pos.x = node.min_x + dx; oldBody.pos.y = node.min_y + dy; retriesCount -= 1; // Make sure we don't bump it out of the box. If we do, next iteration should fix it } while (retriesCount > 0 && isSamePosition(oldBody.pos, body.pos)); if (retriesCount === 0 && isSamePosition(oldBody.pos, body.pos)) { // This is very bad, we ran out of precision. // if we do not return from the method we'll get into // infinite loop here. So we sacrifice correctness of layout, and keep the app running // Next layout iteration should get larger bounding box in the first step and fix this return; } } // Next iteration should subdivide node further. insertStack.push(node, oldBody); insertStack.push(node, body); } } } } } },{}],8:[function(require,module,exports){ /** * Manages a simulation of physical forces acting on bodies and springs. */ module.exports = createPhysicsSimulator; var generateCreateBodyFunction = require('./codeGenerators/generateCreateBody'); var generateQuadTreeFunction = require('./codeGenerators/generateQuadTree'); var generateBoundsFunction = require('./codeGenerators/generateBounds'); var generateCreateDragForceFunction = require('./codeGenerators/generateCreateDragForce'); var generateCreateSpringForceFunction = require('./codeGenerators/generateCreateSpringForce'); var generateIntegratorFunction = require('./codeGenerators/generateIntegrator'); var dimensionalCache = {}; function createPhysicsSimulator(settings) { var Spring = require('./spring'); var merge = require('ngraph.merge'); var eventify = require('ngraph.events'); if (settings) { // Check for names from older versions of the layout if (settings.springCoeff !== undefined) throw new Error('springCoeff was renamed to springCoefficient'); if (settings.dragCoeff !== undefined) throw new Error('dragCoeff was renamed to dragCoefficient'); } settings = merge(settings, { /** * Ideal length for links (springs in physical model). */ springLength: 10, /** * Hook's law coefficient. 1 - solid spring. */ springCoefficient: 0.8, /** * Coulomb's law coefficient. It's used to repel nodes thus should be negative * if you make it positive nodes start attract each other :). */ gravity: -12, /** * Theta coefficient from Barnes Hut simulation. Ranged between (0, 1). * The closer it's to 1 the more nodes algorithm will have to go through. * Setting it to one makes Barnes Hut simulation no different from * brute-force forces calculation (each node is considered). */ theta: 0.8, /** * Drag force coefficient. Used to slow down system, thus should be less than 1. * The closer it is to 0 the less tight system will be. */ dragCoefficient: 0.9, // TODO: Need to rename this to something better. E.g. `dragCoefficient` /** * Default time step (dt) for forces integration */ timeStep : 0.5, /** * Adaptive time step uses average spring length to compute actual time step: * See: https://twitter.com/anvaka/status/1293067160755957760 */ adaptiveTimeStepWeight: 0, /** * This parameter defines number of dimensions of the space where simulation * is performed. */ dimensions: 2, /** * In debug mode more checks are performed, this will help you catch errors * quickly, however for production build it is recommended to turn off this flag * to speed up computation. */ debug: false }); var factory = dimensionalCache[settings.dimensions]; if (!factory) { var dimensions = settings.dimensions; factory = { Body: generateCreateBodyFunction(dimensions, settings.debug), createQuadTree: generateQuadTreeFunction(dimensions), createBounds: generateBoundsFunction(dimensions), createDragForce: generateCreateDragForceFunction(dimensions), createSpringForce: generateCreateSpringForceFunction(dimensions), integrate: generateIntegratorFunction(dimensions), }; dimensionalCache[dimensions] = factory; } var Body = factory.Body; var createQuadTree = factory.createQuadTree; var createBounds = factory.createBounds; var createDragForce = factory.createDragForce; var createSpringForce = factory.createSpringForce; var integrate = factory.integrate; var createBody = pos => new Body(pos); var random = require('ngraph.random').random(42); var bodies = []; // Bodies in this simulation. var springs = []; // Springs in this simulation. var quadTree = createQuadTree(settings, random); var bounds = createBounds(bodies, settings, random); var springForce = createSpringForce(settings, random); var dragForce = createDragForce(settings); var totalMovement = 0; // how much movement we made on last step var forces = []; var forceMap = new Map(); var iterationNumber = 0; addForce('nbody', nbodyForce); addForce('spring', updateSpringForce); var publicApi = { /** * Array of bodies, registered with current simulator * * Note: To add new body, use addBody() method. This property is only * exposed for testing/performance purposes. */ bodies: bodies, quadTree: quadTree, /** * Array of springs, registered with current simulator * * Note: To add new spring, use addSpring() method. This property is only * exposed for testing/performance purposes. */ springs: springs, /** * Returns settings with which current simulator was initialized */ settings: settings, /** * Adds a new force to simulation */ addForce: addForce, /** * Removes a force from the simulation. */ removeForce: removeForce, /** * Returns a map of all registered forces. */ getForces: getForces, /** * Performs one step of force simulation. * * @returns {boolean} true if system is considered stable; False otherwise. */ step: function () { for (var i = 0; i < forces.length; ++i) { forces[i](iterationNumber); } var movement = integrate(bodies, settings.timeStep, settings.adaptiveTimeStepWeight); iterationNumber += 1; return movement; }, /** * Adds body to the system * * @param {ngraph.physics.primitives.Body} body physical body * * @returns {ngraph.physics.primitives.Body} added body */ addBody: function (body) { if (!body) { throw new Error('Body is required'); } bodies.push(body); return body; }, /** * Adds body to the system at given position * * @param {Object} pos position of a body * * @returns {ngraph.physics.primitives.Body} added body */ addBodyAt: function (pos) { if (!pos) { throw new Error('Body position is required'); } var body = createBody(pos); bodies.push(body); return body; }, /** * Removes body from the system * * @param {ngraph.physics.primitives.Body} body to remove * * @returns {Boolean} true if body found and removed. falsy otherwise; */ removeBody: function (body) { if (!body) { return; } var idx = bodies.indexOf(body); if (idx < 0) { return; } bodies.splice(idx, 1); if (bodies.length === 0) { bounds.reset(); } return true; }, /** * Adds a spring to this simulation. * * @returns {Object} - a handle for a spring. If you want to later remove * spring pass it to removeSpring() method. */ addSpring: function (body1, body2, springLength, springCoefficient) { if (!body1 || !body2) { throw new Error('Cannot add null spring to force simulator'); } if (typeof springLength !== 'number') { springLength = -1; // assume global configuration } var spring = new Spring(body1, body2, springLength, springCoefficient >= 0 ? springCoefficient : -1); springs.push(spring); // TODO: could mark simulator as dirty. return spring; }, /** * Returns amount of movement performed on last step() call */ getTotalMovement: function () { return totalMovement; }, /** * Removes spring from the system * * @param {Object} spring to remove. Spring is an object returned by addSpring * * @returns {Boolean} true if spring found and removed. falsy otherwise; */ removeSpring: function (spring) { if (!spring) { return; } var idx = springs.indexOf(spring); if (idx > -1) { springs.splice(idx, 1); return true; } }, getBestNewBodyPosition: function (neighbors) { return bounds.getBestNewPosition(neighbors); }, /** * Returns bounding box which covers all bodies */ getBBox: getBoundingBox, getBoundingBox: getBoundingBox, invalidateBBox: function () { console.warn('invalidateBBox() is deprecated, bounds always recomputed on `getBBox()` call'); }, // TODO: Move the force specific stuff to force gravity: function (value) { if (value !== undefined) { settings.gravity = value; quadTree.options({gravity: value}); return this; } else { return settings.gravity; } }, theta: function (value) { if (value !== undefined) { settings.theta = value; quadTree.options({theta: value}); return this; } else { return settings.theta; } }, /** * Returns pseudo-random number generator instance. */ random: random }; // allow settings modification via public API: expose(settings, publicApi); eventify(publicApi); return publicApi; function getBoundingBox() { bounds.update(); return bounds.box; } function addForce(forceName, forceFunction) { if (forceMap.has(forceName)) throw new Error('Force ' + forceName + ' is already added'); forceMap.set(forceName, forceFunction); forces.push(forceFunction); } function removeForce(forceName) { var forceIndex = forces.indexOf(forceMap.get(forceName)); if (forceIndex < 0) return; forces.splice(forceIndex, 1); forceMap.delete(forceName); } function getForces() { // TODO: Should I trust them or clone the forces? return forceMap; } function nbodyForce(/* iterationUmber */) { if (bodies.length === 0) return; quadTree.insertBodies(bodies); var i = bodies.length; while (i--) { var body = bodies[i]; if (!body.isPinned) { body.reset(); quadTree.updateBodyForce(body); dragForce.update(body); } } } function updateSpringForce() { var i = springs.length; while (i--) { springForce.update(springs[i]); } } } function expose(settings, target) { for (var key in settings) { augment(settings, target, key); } } function augment(source, target, key) { if (!source.hasOwnProperty(key)) return; if (typeof target[key] === 'function') { // this accessor is already defined. Ignore it return; } var sourceIsNumber = Number.isFinite(source[key]); if (sourceIsNumber) { target[key] = function (value) { if (value !== undefined) { if (!Number.isFinite(value)) throw new Error('Value of ' + key + ' should be a valid number.'); source[key] = value; return target; } return source[key]; }; } else { target[key] = function (value) { if (value !== undefined) { source[key] = value; return target; } return source[key]; }; } } },{"./codeGenerators/generateBounds":2,"./codeGenerators/generateCreateBody":3,"./codeGenerators/generateCreateDragForce":4,"./codeGenerators/generateCreateSpringForce":5,"./codeGenerators/generateIntegrator":6,"./codeGenerators/generateQuadTree":7,"./spring":9,"ngraph.events":10,"ngraph.merge":11,"ngraph.random":12}],9:[function(require,module,exports){ module.exports = Spring; /** * Represents a physical spring. Spring connects two bodies, has rest length * stiffness coefficient and optional weight */ function Spring(fromBody, toBody, length, springCoefficient) { this.from = fromBody; this.to = toBody; this.length = length; this.coefficient = springCoefficient; } },{}],10:[function(require,module,exports){ module.exports = function eventify(subject) { validateSubject(subject); var eventsStorage = createEventsStorage(subject); subject.on = eventsStorage.on; subject.off = eventsStorage.off; subject.fire = eventsStorage.fire; return subject; }; function createEventsStorage(subject) { // Store all event listeners to this hash. Key is event name, value is array // of callback records. // // A callback record consists of callback function and its optional context: // { 'eventName' => [{callback: function, ctx: object}] } var registeredEvents = Object.create(null); return { on: function (eventName, callback, ctx) { if (typeof callback !== 'function') { throw new Error('callback is expected to be a function'); } var handlers = registeredEvents[eventName]; if (!handlers) { handlers = registeredEvents[eventName] = []; } handlers.push({callback: callback, ctx: ctx}); return subject; }, off: function (eventName, callback) { var wantToRemoveAll = (typeof eventName === 'undefined'); if (wantToRemoveAll) { // Killing old events storage should be enough in this case: registeredEvents = Object.create(null); return subject; } if (registeredEvents[eventName]) { var deleteAllCallbacksForEvent = (typeof callback !== 'function'); if (deleteAllCallbacksForEvent) { delete registeredEvents[eventName]; } else { var callbacks = registeredEvents[eventName]; for (var i = 0; i < callbacks.length; ++i) { if (callbacks[i].callback === callback) { callbacks.splice(i, 1); } } } } return subject; }, fire: function (eventName) { var callbacks = registeredEvents[eventName]; if (!callbacks) { return subject; } var fireArguments; if (arguments.length > 1) { fireArguments = Array.prototype.splice.call(arguments, 1); } for(var i = 0; i < callbacks.length; ++i) { var callbackInfo = callbacks[i]; callbackInfo.callback.apply(callbackInfo.ctx, fireArguments); } return subject; } }; } function validateSubject(subject) { if (!subject) { throw new Error('Eventify cannot use falsy object as events subject'); } var reservedWords = ['on', 'fire', 'off']; for (var i = 0; i < reservedWords.length; ++i) { if (subject.hasOwnProperty(reservedWords[i])) { throw new Error("Subject cannot be eventified, since it already has property '" + reservedWords[i] + "'"); } } } },{}],11:[function(require,module,exports){ module.exports = merge; /** * Augments `target` with properties in `options`. Does not override * target's properties if they are defined and matches expected type in * options * * @returns {Object} merged object */ function merge(target, options) { var key; if (!target) { target = {}; } if (options) { for (key in options) { if (options.hasOwnProperty(key)) { var targetHasIt = target.hasOwnProperty(key), optionsValueType = typeof options[key], shouldReplace = !targetHasIt || (typeof target[key] !== optionsValueType); if (shouldReplace) { target[key] = options[key]; } else if (optionsValueType === 'object') { // go deep, don't care about loops here, we are simple API!: target[key] = merge(target[key], options[key]); } } } } return target; } },{}],12:[function(require,module,exports){ module.exports = random; // TODO: Deprecate? module.exports.random = random, module.exports.randomIterator = randomIterator /** * Creates seeded PRNG with two methods: * next() and nextDouble() */ function random(inputSeed) { var seed = typeof inputSeed === 'number' ? inputSeed : (+new Date()); return new Generator(seed) } function Generator(seed) { this.seed = seed; } /** * Generates random integer number in the range from 0 (inclusive) to maxValue (exclusive) * * @param maxValue Number REQUIRED. Omitting this number will result in NaN values from PRNG. */ Generator.prototype.next = next; /** * Generates random double number in the range from 0 (inclusive) to 1 (exclusive) * This function is the same as Math.random() (except that it could be seeded) */ Generator.prototype.nextDouble = nextDouble; /** * Returns a random real number uniformly in [0, 1) */ Generator.prototype.uniform = nextDouble; Generator.prototype.gaussian = gaussian; function gaussian() { // use the polar form of the Box-Muller transform // based on https://introcs.cs.princeton.edu/java/23recursion/StdRandom.java var r, x, y; do { x = this.nextDouble() * 2 - 1; y = this.nextDouble() * 2 - 1; r = x * x + y * y; } while (r >= 1 || r === 0); return x * Math.sqrt(-2 * Math.log(r)/r); } function nextDouble() { var seed = this.seed; // Robert Jenkins' 32 bit integer hash function. seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff; seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff; seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff; seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff; seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff; seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff; this.seed = seed; return (seed & 0xfffffff) / 0x10000000; } function next(maxValue) { return Math.floor(this.nextDouble() * maxValue); } /* * Creates iterator over array, which returns items of array in random order * Time complexity is guaranteed to be O(n); */ function randomIterator(array, customRandom) { var localRandom = customRandom || random(); if (typeof localRandom.next !== 'function') { throw new Error('customRandom does not match expected API: next() function is missing'); } return { forEach: forEach, /** * Shuffles array randomly, in place. */ shuffle: shuffle }; function shuffle() { var i, j, t; for (i = array.length - 1; i > 0; --i) { j = localRandom.next(i + 1); // i inclusive t = array[j]; array[j] = array[i]; array[i] = t; } return array; } function forEach(callback) { var i, j, t; for (i = array.length - 1; i > 0; --i) { j = localRandom.next(i + 1); // i inclusive t = array[j]; array[j] = array[i]; array[i] = t; callback(t); } if (array.length) { callback(array[0]); } } } },{}]},{},[1])(1) });