(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else if(typeof exports === 'object')
		exports["cytoscapeAutomove"] = factory();
	else
		root["cytoscapeAutomove"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// identity function for calling harmony imports with the correct context
/******/ 	__webpack_require__.i = function(value) { return value; };
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var defaults = __webpack_require__(2);

var typeofStr = _typeof('');
var typeofObj = _typeof({});
var typeofFn = _typeof(function () {});

var isObject = function isObject(x) {
  return (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === typeofObj;
};
var isString = function isString(x) {
  return (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === typeofStr;
};
var isFunction = function isFunction(x) {
  return (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === typeofFn;
};
var isCollection = function isCollection(x) {
  return isObject(x) && isFunction(x.collection);
};

// Object.assign() polyfill
var assign = __webpack_require__(1);

var eleExists = function eleExists(ele) {
  return ele != null && !ele.removed();
};

var _elesHasEle = function elesHasEle(eles, ele) {
  if (eles.has != undefined) {
    // 3.x
    _elesHasEle = function elesHasEle(eles, ele) {
      return eles.has(ele);
    };
  } else {
    // 2.x
    _elesHasEle = function elesHasEle(eles, ele) {
      return eles.intersection(ele).length > 0;
    };
  }

  return _elesHasEle(eles, ele);
};

var getEleMatchesSpecFn = function getEleMatchesSpecFn(spec) {
  if (isString(spec)) {
    return function (ele) {
      return ele.is(spec);
    };
  } else if (isFunction(spec)) {
    return spec;
  } else if (isCollection(spec)) {
    return function (ele) {
      return _elesHasEle(spec, ele);
    };
  } else {
    throw new Error('Can not create match function for spec', spec);
  }
};

var bindings = [];

var bind = function bind(cy, events, selector, fn) {
  var b = { cy: cy, events: events, selector: selector || 'node', fn: fn };

  bindings.push(b);

  cy.on(b.events, b.selector, b.fn);

  return b;
};

var bindOnRule = function bindOnRule(rule, cy, events, selector, fn) {
  var b = bind(cy, events, selector, fn);
  var bindings = rule.bindings = rule.bindings || [];

  bindings.push(b);
};

var unbindAllOnRule = function unbindAllOnRule(rule) {
  var unbind = function unbind(b) {
    b.cy.off(b.events, b.selector, b.fn);
  };

  rule.bindings.forEach(unbind);

  rule.bindings = [];
};

var getRepositioner = function getRepositioner(rule, cy) {
  var r = rule.reposition;

  if (r === 'mean') {
    return meanNeighborhoodPosition(getEleMatchesSpecFn(rule.meanIgnores));
  } else if (r === 'viewport') {
    return viewportPosition(cy);
  } else if (r === 'drag') {
    return dragAlong(rule);
  } else if (isObject(r)) {
    if (r.type == undefined || r.type == "inside") {
      return boxPosition(r);
    } else if (r.type == "outside") {
      return outsideBoxPosition(r);
    }
  } else {
    return r;
  }
};

var dragAlong = function dragAlong(rule) {
  return function (node) {
    var pos = node.position();
    var delta = rule.delta;

    if (rule.delta != null && !node.same(rule.grabbedNode) && !node.grabbed()) {
      return {
        x: pos.x + delta.x,
        y: pos.y + delta.y
      };
    }
  };
};

var meanNeighborhoodPosition = function meanNeighborhoodPosition(ignore) {
  return function (node) {
    var nhood = node.neighborhood();
    var avgPos = { x: 0, y: 0 };
    var nhoodSize = 0;

    for (var i = 0; i < nhood.length; i++) {
      var nhoodEle = nhood[i];

      if (nhoodEle.isNode() && !ignore(nhoodEle)) {
        var pos = nhoodEle.position();

        avgPos.x += pos.x;
        avgPos.y += pos.y;

        nhoodSize++;
      }
    }

    // the position should remain unchanged if we would stack the nodes on top of each other
    if (nhoodSize < 2) {
      return undefined;
    }

    avgPos.x /= nhoodSize;
    avgPos.y /= nhoodSize;

    return avgPos;
  };
};

var constrain = function constrain(val, min, max) {
  return val < min ? min : val > max ? max : val;
};

var constrainInBox = function constrainInBox(node, bb) {
  var pos = node.position();

  return {
    x: constrain(pos.x, bb.x1, bb.x2),
    y: constrain(pos.y, bb.y1, bb.y2)
  };
};

var boxPosition = function boxPosition(bb) {
  return function (node) {
    return constrainInBox(node, bb);
  };
};

var constrainOutsideBox = function constrainOutsideBox(node, bb) {
  var pos = node.position();
  var x = pos.x,
      y = pos.y;
  var x1 = bb.x1,
      y1 = bb.y1,
      x2 = bb.x2,
      y2 = bb.y2;

  var inX = x1 <= x && x <= x2;
  var inY = y1 <= y && y <= y2;
  var abs = Math.abs;

  if (inX && inY) {
    // inside
    var dx1 = abs(x1 - x);
    var dx2 = abs(x2 - x);
    var dy1 = abs(y1 - y);
    var dy2 = abs(y2 - y);
    var min = Math.min(dx1, dx2, dy1, dy2); // which side of box is closest?

    // get position outside, by closest side of box
    if (min === dx1) {
      return { x: x1, y: y };
    } else if (min === dx2) {
      return { x: x2, y: y };
    } else if (min === dy1) {
      return { x: x, y: y1 };
    } else {
      // min === dy2
      return { x: x, y: y2 };
    }
  } else {
    // outside already
    return { x: x, y: y };
  }
};

var outsideBoxPosition = function outsideBoxPosition(bb) {
  return function (node) {
    return constrainOutsideBox(node, bb);
  };
};

var viewportPosition = function viewportPosition(cy) {
  return function (node) {
    var extent = cy.extent();
    var w = node.outerWidth();
    var h = node.outerHeight();
    var bb = {
      x1: extent.x1 + w / 2,
      x2: extent.x2 - w / 2,
      y1: extent.y1 + h / 2,
      y2: extent.y2 - h / 2
    };

    return constrainInBox(node, bb);
  };
};

var meanListener = function meanListener(rule) {
  return function (update, cy) {
    var matches = function matches(ele) {
      // must meet ele set and be connected to more than (1 edge + 1 node)
      return rule.matches(ele) && ele.neighborhood().length > 2 && !ele.grabbed();
    };

    bindOnRule(rule, cy, 'position', 'node', function () {
      var movedNode = this;

      if (movedNode.neighborhood().some(matches) || rule.meanOnSelfPosition(movedNode) && matches(movedNode)) {
        update(cy, [rule]);
      }
    });

    bindOnRule(rule, cy, 'add remove', 'edge', function () {
      var edge = this;
      var src = cy.getElementById(edge.data('source'));
      var tgt = cy.getElementById(edge.data('target'));

      if ([src, tgt].some(matches)) {
        update(cy, [rule]);
      }
    });
  };
};

var dragListener = function dragListener(rule) {
  return function (update, cy) {
    bindOnRule(rule, cy, 'grab', 'node', function () {
      var node = this;

      if (rule.dragWithMatches(node)) {
        var p = node.position();

        rule.grabbedNode = node;
        rule.p1 = { x: p.x, y: p.y };
        rule.delta = { x: 0, y: 0 };
      }
    });

    bindOnRule(rule, cy, 'drag', 'node', function () {
      var node = this;

      if (node.same(rule.grabbedNode)) {
        var d = rule.delta;
        var p1 = rule.p1;
        var p = node.position();
        var p2 = { x: p.x, y: p.y };

        d.x = p2.x - p1.x;
        d.y = p2.y - p1.y;

        rule.p1 = p2;

        update(cy, [rule]);
      }
    });

    bindOnRule(rule, cy, 'free', 'node', function () {
      rule.grabbedNode = null;
      rule.delta = null;
      rule.p1 = null;
    });
  };
};

var matchingNodesListener = function matchingNodesListener(rule) {
  return function (update, cy) {
    bindOnRule(rule, cy, 'position', 'node', function () {
      var movedNode = this;

      if (rule.matches(movedNode)) {
        update(cy, [rule]);
      }
    });
  };
};

var getListener = function getListener(cy, rule) {
  if (rule.reposition === 'mean') {
    return meanListener(rule);
  } else if (rule.reposition === 'drag') {
    return dragListener(rule);
  } else if (isObject(rule.reposition) || rule.when === 'matching' || rule.reposition === 'viewport') {
    return matchingNodesListener(rule);
  } else {
    return rule.when;
  }
};

var addRule = function addRule(cy, scratch, options) {
  var rule = assign({}, defaults, options);

  rule.getNewPos = getRepositioner(rule, cy);
  rule.listener = getListener(cy, rule);

  var nodesAreCollection = isCollection(rule.nodesMatching);

  if (nodesAreCollection) {
    rule.nodes = rule.nodesMatching.slice();

    rule.matches = function (ele) {
      return eleExists(ele) && _elesHasEle(rule.nodes, ele);
    };
  } else {
    var matches = getEleMatchesSpecFn(rule.nodesMatching);

    rule.matches = function (ele) {
      return eleExists(ele) && matches(ele);
    };
  }

  if (rule.dragWith != null) {
    rule.dragWithMatches = getEleMatchesSpecFn(rule.dragWith);
  }

  rule.listener(function () {
    update(cy, [rule]);
  }, cy);

  rule.enabled = true;

  scratch.rules.push(rule);

  return rule;
};

var bindForNodeList = function bindForNodeList(cy, scratch) {
  scratch.onAddNode = function (evt) {
    var target = evt.target;

    scratch.nodes.merge(target);
  };

  scratch.onRmNode = function (evt) {
    var target = evt.target;

    scratch.nodes.unmerge(target);
  };

  cy.on('add', 'node', scratch.onAddNode);
  cy.on('remove', 'node', scratch.onRmNode);
};

var unbindForNodeList = function unbindForNodeList(cy, scratch) {
  cy.removeListener('add', 'node', scratch.onAddNode);
  cy.removeListener('remove', 'node', scratch.onRmNode);
};

var update = function update(cy, rules) {
  var scratch = cy.scratch().automove;

  rules = rules != null ? rules : scratch.rules;

  cy.batch(function () {
    // batch for performance
    for (var i = 0; i < rules.length; i++) {
      var rule = rules[i];

      if (rule.destroyed || !rule.enabled) {
        break;
      } // ignore destroyed rules b/c user may use custom when()

      var nodes = rule.nodes || scratch.nodes;

      for (var j = nodes.length - 1; j >= 0; j--) {
        var node = nodes[j];

        if (node.removed()) {
          // remove from list for perf
          nodes.unmerge(node);
          continue;
        }

        if (!rule.matches(node)) {
          continue;
        }

        var pos = node.position();
        var newPos = rule.getNewPos(node);
        var newPosIsDiff = newPos != null && (pos.x !== newPos.x || pos.y !== newPos.y);

        if (newPosIsDiff) {
          // only update on diff for perf
          node.position(newPos);

          node.trigger('automove', [rule]);
        }
      }
    }
  });
};

var automove = function automove(options) {
  var cy = this;

  var scratch = cy.scratch().automove = cy.scratch().automove || {
    rules: []
  };

  if (scratch.rules.length === 0) {
    scratch.nodes = cy.nodes().slice();

    bindForNodeList(cy, scratch);
  }

  if (options === 'destroy') {
    scratch.rules.forEach(function (r) {
      r.destroy();
    });
    scratch.rules.splice(0, scratch.rules.length);

    unbindForNodeList(cy, scratch);

    return;
  }

  var rule = addRule(cy, scratch, options);

  update(cy, [rule]); // do an initial update to make sure the start state is correct

  return {
    apply: function apply() {
      update(cy, [rule]);
    },

    disable: function disable() {
      this.toggle(false);
    },

    enable: function enable() {
      this.toggle(true);
    },

    enabled: function enabled() {
      return rule.enabled;
    },

    toggle: function toggle(on) {
      rule.enabled = on !== undefined ? on : !rule.enabled;

      if (rule.enabled) {
        update(cy, [rule]);
      }
    },

    destroy: function destroy() {
      var rules = scratch.rules;

      unbindAllOnRule(rule);

      rule.destroyed = true;

      rules.splice(rules.indexOf(rule), 1);

      if (rules.length === 0) {
        unbindForNodeList(cy, scratch);
      }

      return this;
    }
  };
};

module.exports = automove;

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// Simple, internal Object.assign() polyfill for options objects etc.

module.exports = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
  for (var _len = arguments.length, srcs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
    srcs[_key - 1] = arguments[_key];
  }

  srcs.forEach(function (src) {
    Object.keys(src).forEach(function (k) {
      return tgt[k] = src[k];
    });
  });

  return tgt;
};

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* eslint-disable no-unused-vars */
var defaults = {
  // specify nodes that should be automoved with one of
  // - a function that returns true for matching nodes
  // - a selector that matches the nodes
  // - a collection of nodes (very good for performance)
  nodesMatching: function nodesMatching(node) {
    return false;
  },

  // specify how a node's position should be updated with one of
  // - function( node ){ return { x: 1, y: 2 }; } => put the node where the function returns
  // - { x1, y1, x2, y2 } => constrain the node position within the bounding box (in model co-ordinates)
  // - { x1, y1, x2, y2, type: 'inside' } => constrain the node position within the bounding box (in model co-ordinates)
  // - { x1, y1, x2, y2, type: 'outside' } => constrain the node position outside the bounding box (in model co-ordinates)
  // - 'mean' => put the node in the average position of its neighbourhood
  // - 'viewport' => keeps the node body within the viewport
  // - 'drag' => matching nodes are effectively dragged along
  reposition: 'mean',

  // specify when the repositioning should occur by specifying a function that
  // calls update() when reposition updates should occur
  // - function( update ){ /* ... */ update(); } => a manual function for updating
  // - 'matching' => automatically update on position events for nodesMatching
  // - set efficiently and automatically for
  //   - reposition: 'mean'
  //   - reposition: { x1, y1, x2, y2 }
  //   - reposition: 'viewport'
  //   - reposition: 'drag'
  // - default/undefined => on a position event for any node (not as efficient...)
  when: undefined,

  //
  // customisation options for non-function `reposition` values
  //

  // `reposition: 'mean'`

  // specify nodes that should be ignored in the mean calculation
  // - a function that returns true for nodes to be ignored
  // - a selector that matches the nodes to be ignored
  // - a collection of nodes to be ignored (very good for performance)
  meanIgnores: function meanIgnores(node) {
    return false;
  },

  // specify whether moving a particular `nodesMatching` node causes repositioning
  // - true : the mid node can't be independently moved/dragged
  // - false : the mid node can be independently moved/dragged (useful if you want the mid node to use `reposition: 'drag' in another rule with its neighbourhood`)
  meanOnSelfPosition: function meanOnSelfPosition(node) {
    return true;
  },

  // `reposition: 'drag'`

  // specify nodes that when dragged cause the matched nodes to move along (i.e. the master nodes)
  // - a function that returns true for nodes to be listened to for drag events
  // - a selector that matches the nodes to be listened to for drag events
  // - a collection of nodes to be listened to for drag events (very good for performance)
  dragWith: function dragWith(node) {
    return false;
  }
};

/* eslint-enable */

module.exports = defaults;

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var automove = __webpack_require__(0);

// registers the extension on a cytoscape lib ref
var register = function register(cytoscape) {
  if (!cytoscape) {
    return;
  } // can't register if cytoscape unspecified

  cytoscape('core', 'automove', automove); // register with cytoscape.js
};

if (typeof cytoscape !== 'undefined') {
  // expose to global cytoscape (i.e. window.cytoscape)
  register(cytoscape);
}

module.exports = register;

/***/ })
/******/ ]);
});