From 35265028acfc73b63d2b7c1c9711214df014cbea Mon Sep 17 00:00:00 2001 From: Max Franz Date: Fri, 16 Feb 2018 14:52:00 -0500 Subject: [PATCH] Preparing to publish 3.2.9 --- dist/cytoscape.cjs.js | 38763 +++++++++++++------------- dist/cytoscape.js | 40350 ++++++++++++++-------------- dist/cytoscape.min.js | 8 +- documentation/docmaker.json | 2 +- documentation/index.html | 12 +- documentation/js/cytoscape.min.js | 8 +- package.json | 2 +- src/version.js | 2 +- 8 files changed, 39100 insertions(+), 40047 deletions(-) diff --git a/dist/cytoscape.cjs.js b/dist/cytoscape.cjs.js index 403eee51e5..83c22e1a78 100644 --- a/dist/cytoscape.cjs.js +++ b/dist/cytoscape.cjs.js @@ -1,13 +1,13 @@ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(require("heap"), require("lodash.debounce")); + module.exports = factory(require("lodash.debounce"), require("heap")); else if(typeof define === 'function' && define.amd) - define(["heap", "lodash.debounce"], factory); + define(["lodash.debounce", "heap"], factory); else if(typeof exports === 'object') - exports["cytoscape"] = factory(require("heap"), require("lodash.debounce")); + exports["cytoscape"] = factory(require("lodash.debounce"), require("heap")); else - root["cytoscape"] = factory(root["heap"], root["lodash.debounce"]); -})(this, function(__WEBPACK_EXTERNAL_MODULE_140__, __WEBPACK_EXTERNAL_MODULE_141__) { + root["cytoscape"] = factory(root["lodash.debounce"], root["heap"]); +})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_26__, __WEBPACK_EXTERNAL_MODULE_32__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; @@ -43,9 +43,6 @@ return /******/ (function(modules) { // webpackBootstrap /******/ // 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)) { @@ -73,7 +70,7 @@ return /******/ (function(modules) { // webpackBootstrap /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 116); +/******/ return __webpack_require__(__webpack_require__.s = 20); /******/ }) /************************************************************************/ /******/ ([ @@ -87,7 +84,7 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol /*global HTMLElement DocumentTouch */ -var window = __webpack_require__(4); +var window = __webpack_require__(3); var navigator = window ? window.navigator : null; var document = window ? window.document : null; @@ -434,7 +431,7 @@ util.setPrefixedProperty = function (obj, propName, prefix, value) { obj[propName] = value; }; -[__webpack_require__(130), __webpack_require__(131), { memoize: __webpack_require__(19) }, __webpack_require__(132), __webpack_require__(134), __webpack_require__(135), __webpack_require__(133)].forEach(function (req) { +[__webpack_require__(21), __webpack_require__(22), { memoize: __webpack_require__(13) }, __webpack_require__(23), __webpack_require__(24), __webpack_require__(25), __webpack_require__(27)].forEach(function (req) { util.extend(util, req); }); @@ -1530,6 +1527,15 @@ module.exports = math; "use strict"; +module.exports = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + // use this module to cherry pick functions into your prototype // (useful for functions shared between the core and collections, for example) @@ -1540,27 +1546,18 @@ var util = __webpack_require__(1); var define = {}; -[__webpack_require__(72), __webpack_require__(73), __webpack_require__(74)].forEach(function (m) { +[__webpack_require__(43), __webpack_require__(45), __webpack_require__(46)].forEach(function (m) { util.assign(define, m); }); module.exports = define; -/***/ }), -/* 4 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef - /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* WEBPACK VAR INJECTION */(function(setImmediate) { + 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; }; @@ -1772,7 +1769,6 @@ api.reject = function (val) { }; module.exports = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(138).setImmediate)) /***/ }), /* 6 */ @@ -1783,7 +1779,7 @@ module.exports = typeof Promise !== 'undefined' ? Promise : api; // eslint-disab var is = __webpack_require__(0); var util = __webpack_require__(1); -var newQuery = __webpack_require__(11); +var newQuery = __webpack_require__(10); var Selector = function Selector(selector) { var self = this; @@ -1973,7 +1969,7 @@ selfn.toString = selfn.selector = function () { return str; }; -[__webpack_require__(120), __webpack_require__(119)].forEach(function (p) { +[__webpack_require__(49), __webpack_require__(52)].forEach(function (p) { return util.assign(selfn, p); }); @@ -1988,10 +1984,10 @@ module.exports = Selector; var util = __webpack_require__(1); var is = __webpack_require__(0); -var Map = __webpack_require__(117); -var Set = __webpack_require__(9); +var Map = __webpack_require__(28); +var Set = __webpack_require__(8); -var Element = __webpack_require__(13); +var Element = __webpack_require__(14); // factory for generating edge ids when no id is specified for a new element var idFactory = { @@ -2717,7 +2713,7 @@ elesfn.move = function (struct) { return this; // if nothing done }; -[__webpack_require__(32), __webpack_require__(36), __webpack_require__(37), __webpack_require__(38), __webpack_require__(39), __webpack_require__(40), __webpack_require__(41), __webpack_require__(44), __webpack_require__(47), __webpack_require__(48), __webpack_require__(49), __webpack_require__(7), __webpack_require__(50), __webpack_require__(51), __webpack_require__(52), __webpack_require__(53), __webpack_require__(54)].forEach(function (props) { +[__webpack_require__(29), __webpack_require__(42), __webpack_require__(47), __webpack_require__(48), __webpack_require__(53), __webpack_require__(54), __webpack_require__(55), __webpack_require__(56), __webpack_require__(61), __webpack_require__(62), __webpack_require__(63), __webpack_require__(7), __webpack_require__(64), __webpack_require__(65), __webpack_require__(66), __webpack_require__(67), __webpack_require__(68)].forEach(function (props) { util.extend(elesfn, props); }); @@ -2730,15 +2726,6 @@ module.exports = Collection; "use strict"; -module.exports = __webpack_require__(140); - -/***/ }), -/* 9 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 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; }; @@ -2825,6 +2812,15 @@ var ObjectSet = function () { module.exports = ObjectSet; +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = __webpack_require__(32); + /***/ }), /* 10 */ /***/ (function(module, exports, __webpack_require__) { @@ -2832,9 +2828,44 @@ module.exports = ObjectSet; "use strict"; +// storage for parsed queries +var newQuery = function newQuery() { + return { + classes: [], + colonSelectors: [], + data: [], + group: null, + ids: [], + meta: [], + + // fake selectors + collection: null, // a collection to match against + filter: null, // filter function + + // these are defined in the upward direction rather than down (e.g. child) + // because we need to go up in Selector.filter() + parent: null, // parent query obj + ancestor: null, // ancestor query obj + subject: null, // defines subject in compound query (subject query obj; points to self if subject) + + // use these only when subject has been defined + child: null, + descendant: null + }; +}; + +module.exports = newQuery; + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + var util = __webpack_require__(1); var is = __webpack_require__(0); -var Event = __webpack_require__(15); +var Event = __webpack_require__(16); var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace") var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally @@ -3066,41 +3097,6 @@ p.emit = p.trigger = function (events, extraParams, manualCallback) { module.exports = Emitter; -/***/ }), -/* 11 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -// storage for parsed queries -var newQuery = function newQuery() { - return { - classes: [], - colonSelectors: [], - data: [], - group: null, - ids: [], - meta: [], - - // fake selectors - collection: null, // a collection to match against - filter: null, // filter function - - // these are defined in the upward direction rather than down (e.g. child) - // because we need to go up in Selector.filter() - parent: null, // parent query obj - ancestor: null, // ancestor query obj - subject: null, // defines subject in compound query (subject query obj; points to self if subject) - - // use these only when subject has been defined - child: null, - descendant: null - }; -}; - -module.exports = newQuery; - /***/ }), /* 12 */ /***/ (function(module, exports, __webpack_require__) { @@ -3108,12 +3104,12 @@ module.exports = newQuery; "use strict"; -var window = __webpack_require__(4); +var window = __webpack_require__(3); var util = __webpack_require__(1); var Collection = __webpack_require__(7); var is = __webpack_require__(0); var Promise = __webpack_require__(5); -var define = __webpack_require__(3); +var define = __webpack_require__(4); var Core = function Core(opts) { var cy = this; @@ -3542,7 +3538,7 @@ util.extend(corefn, { corefn.$id = corefn.getElementById; -[__webpack_require__(55), __webpack_require__(59), __webpack_require__(64), __webpack_require__(65), __webpack_require__(66), __webpack_require__(67), __webpack_require__(68), __webpack_require__(69), __webpack_require__(70), __webpack_require__(71)].forEach(function (props) { +[__webpack_require__(69), __webpack_require__(70), __webpack_require__(78), __webpack_require__(79), __webpack_require__(80), __webpack_require__(81), __webpack_require__(82), __webpack_require__(83), __webpack_require__(84), __webpack_require__(93)].forEach(function (props) { util.extend(corefn, props); }); @@ -3555,9 +3551,54 @@ module.exports = Core; "use strict"; +module.exports = function memoize(fn, keyFn) { + if (!keyFn) { + keyFn = function keyFn() { + if (arguments.length === 1) { + return arguments[0]; + } else if (arguments.length === 0) { + return 'undefined'; + } + + var args = []; + + for (var i = 0; i < arguments.length; i++) { + args.push(arguments[i]); + } + + return args.join('$'); + }; + } + + var memoizedFn = function memoizedFn() { + var self = this; + var args = arguments; + var ret = void 0; + var k = keyFn.apply(self, args); + var cache = memoizedFn.cache; + + if (!(ret = cache[k])) { + ret = cache[k] = fn.apply(self, args); + } + + return ret; + }; + + memoizedFn.cache = {}; + + return memoizedFn; +}; + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + var util = __webpack_require__(1); var is = __webpack_require__(0); -var Set = __webpack_require__(9); +var Set = __webpack_require__(8); // represents a node or an edge var Element = function Element(cy, params, restore) { @@ -3663,313 +3704,38 @@ var Element = function Element(cy, params, restore) { module.exports = Element; /***/ }), -/* 14 */ +/* 15 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/** - * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges), - * and z-index (low to high). These styles affect how this applies: - * - * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the - * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from - * root to leaves of the compound graph. The last drawn is `top`. - * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes. - * `manual` ignores this convention and draws based on the `z-index` value setting. - * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher - * `z-index` will be drawn on top of an element with a lower `z-index`. - */ var util = __webpack_require__(1); -var zIndexSort = function zIndexSort(a, b) { - var cy = a.cy(); - var hasCompoundNodes = cy.hasCompoundNodes(); - - function getDepth(ele) { - var style = ele.pstyle('z-compound-depth'); - if (style.value === 'auto') { - return hasCompoundNodes ? ele.zDepth() : 0; - } else if (style.value === 'bottom') { - return -1; - } else if (style.value === 'top') { - return util.MAX_INT; - } - // 'orphan' - return 0; +var stateSelectors = [{ + selector: ':selected', + matches: function matches(ele) { + return ele.selected(); } - var depthDiff = getDepth(a) - getDepth(b); - if (depthDiff !== 0) { - return depthDiff; +}, { + selector: ':unselected', + matches: function matches(ele) { + return !ele.selected(); } - - function getEleDepth(ele) { - var style = ele.pstyle('z-index-compare'); - if (style.value === 'auto') { - return ele.isNode() ? 1 : 0; - } - // 'manual' - return 0; +}, { + selector: ':selectable', + matches: function matches(ele) { + return ele.selectable(); } - var eleDiff = getEleDepth(a) - getEleDepth(b); - if (eleDiff !== 0) { - return eleDiff; +}, { + selector: ':unselectable', + matches: function matches(ele) { + return !ele.selectable(); } - - var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value; - if (zDiff !== 0) { - return zDiff; - } - // compare indices in the core (order added to graph w/ last on top) - return a.poolIndex() - b.poolIndex(); -}; - -module.exports = zIndexSort; - -/***/ }), -/* 15 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -/*! -Event object based on jQuery events, MIT license - -https://jquery.org/license/ -https://tldrlegal.com/license/mit-license -https://github.com/jquery/jquery/blob/master/src/event.js -*/ - -var Event = function Event(src, props) { - this.recycle(src, props); -}; - -function returnFalse() { - return false; -} - -function returnTrue() { - return true; -} - -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -Event.prototype = { - instanceString: function instanceString() { - return 'event'; - }, - - recycle: function recycle(src, props) { - this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse; - - if (src != null && src.preventDefault) { - // Browser Event object - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse; - } else if (src != null && src.type) { - // Plain object containing all event details - props = src; - } else { - // Event string - this.type = src; - } - - // Put explicitly provided properties onto the event object - if (props != null) { - // more efficient to manually copy fields we use - this.originalEvent = props.originalEvent; - this.type = props.type != null ? props.type : this.type; - this.cy = props.cy; - this.target = props.target; - this.position = props.position; - this.renderedPosition = props.renderedPosition; - this.namespace = props.namespace; - this.layout = props.layout; - } - - if (this.cy != null && this.position != null && this.renderedPosition == null) { - // create a rendered position based on the passed position - var pos = this.position; - var zoom = this.cy.zoom(); - var pan = this.cy.pan(); - - this.renderedPosition = { - x: pos.x * zoom + pan.x, - y: pos.y * zoom + pan.y - }; - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); - }, - - preventDefault: function preventDefault() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if (!e) { - return; - } - - // if preventDefault exists run it on the original event - if (e.preventDefault) { - e.preventDefault(); - } - }, - - stopPropagation: function stopPropagation() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if (!e) { - return; - } - - // if stopPropagation exists run it on the original event - if (e.stopPropagation) { - e.stopPropagation(); - } - }, - - stopImmediatePropagation: function stopImmediatePropagation() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -module.exports = Event; - -/***/ }), -/* 16 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(1); - -var fullFpsTime = 1000 / 60; // assume 60 frames per second - -module.exports = { - setupDequeueing: function setupDequeueing(opts) { - return function setupDequeueingImpl() { - var self = this; - var r = this.renderer; - - if (self.dequeueingSetup) { - return; - } else { - self.dequeueingSetup = true; - } - - var queueRedraw = util.debounce(function () { - r.redrawHint('eles', true); - r.redrawHint('drag', true); - - r.redraw(); - }, opts.deqRedrawThreshold); - - var dequeue = function dequeue(willDraw, frameStartTime) { - var startTime = util.performanceNow(); - var avgRenderTime = r.averageRedrawTime; - var renderTime = r.lastRedrawTime; - var deqd = []; - var extent = r.cy.extent(); - var pixelRatio = r.getPixelRatio(); - - while (true) { - var now = util.performanceNow(); - var duration = now - startTime; - var frameDuration = now - frameStartTime; - - if (renderTime < fullFpsTime) { - // if we're rendering faster than the ideal fps, then do dequeueing - // during all of the remaining frame time - - var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0); - - if (frameDuration >= opts.deqFastCost * timeAvailable) { - break; - } - } else { - if (willDraw) { - if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) { - break; - } - } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) { - break; - } - } - - var thisDeqd = opts.deq(self, pixelRatio, extent); - - if (thisDeqd.length > 0) { - for (var i = 0; i < thisDeqd.length; i++) { - deqd.push(thisDeqd[i]); - } - } else { - break; - } - } - - // callbacks on dequeue - if (deqd.length > 0) { - opts.onDeqd(self, deqd); - - if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) { - queueRedraw(); - } - } - }; - - var priority = opts.priority || util.noop; - - r.beforeRender(dequeue, priority(self)); - }; - } -}; - -/***/ }), -/* 17 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(1); - -var stateSelectors = [{ - selector: ':selected', - matches: function matches(ele) { - return ele.selected(); - } -}, { - selector: ':unselected', - matches: function matches(ele) { - return !ele.selected(); - } -}, { - selector: ':selectable', - matches: function matches(ele) { - return ele.selectable(); - } -}, { - selector: ':unselectable', - matches: function matches(ele) { - return !ele.selectable(); - } -}, { - selector: ':locked', - matches: function matches(ele) { - return ele.locked(); +}, { + selector: ':locked', + matches: function matches(ele) { + return ele.locked(); } }, { selector: ':unlocked', @@ -4115,38 +3881,223 @@ var stateSelectorRegex = '(' + stateSelectors.map(function (s) { module.exports = { stateSelectors: stateSelectors, stateSelectorMatches: stateSelectorMatches, stateSelectorRegex: stateSelectorRegex }; /***/ }), -/* 18 */ +/* 16 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var is = __webpack_require__(0); -var util = __webpack_require__(1); -var Selector = __webpack_require__(6); - -var Style = function Style(cy) { +/*! +Event object based on jQuery events, MIT license - if (!(this instanceof Style)) { - return new Style(cy); - } +https://jquery.org/license/ +https://tldrlegal.com/license/mit-license +https://github.com/jquery/jquery/blob/master/src/event.js +*/ - if (!is.core(cy)) { - util.error('A style must have a core reference'); - return; - } +var Event = function Event(src, props) { + this.recycle(src, props); +}; - this._private = { - cy: cy, - coreStyle: {} - }; +function returnFalse() { + return false; +} - this.length = 0; +function returnTrue() { + return true; +} - this.resetToDefault(); -}; +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +Event.prototype = { + instanceString: function instanceString() { + return 'event'; + }, -var styfn = Style.prototype; + recycle: function recycle(src, props) { + this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse; + + if (src != null && src.preventDefault) { + // Browser Event object + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse; + } else if (src != null && src.type) { + // Plain object containing all event details + props = src; + } else { + // Event string + this.type = src; + } + + // Put explicitly provided properties onto the event object + if (props != null) { + // more efficient to manually copy fields we use + this.originalEvent = props.originalEvent; + this.type = props.type != null ? props.type : this.type; + this.cy = props.cy; + this.target = props.target; + this.position = props.position; + this.renderedPosition = props.renderedPosition; + this.namespace = props.namespace; + this.layout = props.layout; + } + + if (this.cy != null && this.position != null && this.renderedPosition == null) { + // create a rendered position based on the passed position + var pos = this.position; + var zoom = this.cy.zoom(); + var pan = this.cy.pan(); + + this.renderedPosition = { + x: pos.x * zoom + pan.x, + y: pos.y * zoom + pan.y + }; + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + }, + + preventDefault: function preventDefault() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if (!e) { + return; + } + + // if preventDefault exists run it on the original event + if (e.preventDefault) { + e.preventDefault(); + } + }, + + stopPropagation: function stopPropagation() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if (!e) { + return; + } + + // if stopPropagation exists run it on the original event + if (e.stopPropagation) { + e.stopPropagation(); + } + }, + + stopImmediatePropagation: function stopImmediatePropagation() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +module.exports = Event; + +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges), + * and z-index (low to high). These styles affect how this applies: + * + * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the + * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from + * root to leaves of the compound graph. The last drawn is `top`. + * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes. + * `manual` ignores this convention and draws based on the `z-index` value setting. + * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher + * `z-index` will be drawn on top of an element with a lower `z-index`. + */ +var util = __webpack_require__(1); + +var zIndexSort = function zIndexSort(a, b) { + var cy = a.cy(); + var hasCompoundNodes = cy.hasCompoundNodes(); + + function getDepth(ele) { + var style = ele.pstyle('z-compound-depth'); + if (style.value === 'auto') { + return hasCompoundNodes ? ele.zDepth() : 0; + } else if (style.value === 'bottom') { + return -1; + } else if (style.value === 'top') { + return util.MAX_INT; + } + // 'orphan' + return 0; + } + var depthDiff = getDepth(a) - getDepth(b); + if (depthDiff !== 0) { + return depthDiff; + } + + function getEleDepth(ele) { + var style = ele.pstyle('z-index-compare'); + if (style.value === 'auto') { + return ele.isNode() ? 1 : 0; + } + // 'manual' + return 0; + } + var eleDiff = getEleDepth(a) - getEleDepth(b); + if (eleDiff !== 0) { + return eleDiff; + } + + var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value; + if (zDiff !== 0) { + return zDiff; + } + // compare indices in the core (order added to graph w/ last on top) + return a.poolIndex() - b.poolIndex(); +}; + +module.exports = zIndexSort; + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var is = __webpack_require__(0); +var util = __webpack_require__(1); +var Selector = __webpack_require__(6); + +var Style = function Style(cy) { + + if (!(this instanceof Style)) { + return new Style(cy); + } + + if (!is.core(cy)) { + util.error('A style must have a core reference'); + return; + } + + this._private = { + cy: cy, + coreStyle: {} + }; + + this.length = 0; + + this.resetToDefault(); +}; + +var styfn = Style.prototype; styfn.instanceString = function () { return 'style'; @@ -4284,7 +4235,7 @@ Style.fromString = function (cy, string) { return new Style(cy).fromString(string); }; -[__webpack_require__(122), __webpack_require__(123), __webpack_require__(124), __webpack_require__(125), __webpack_require__(126), __webpack_require__(129), __webpack_require__(128), __webpack_require__(127)].forEach(function (props) { +[__webpack_require__(85), __webpack_require__(86), __webpack_require__(87), __webpack_require__(88), __webpack_require__(89), __webpack_require__(90), __webpack_require__(91), __webpack_require__(92)].forEach(function (props) { util.extend(styfn, props); }); @@ -4300,670 +4251,609 @@ module.exports = Style; "use strict"; -module.exports = function memoize(fn, keyFn) { - if (!keyFn) { - keyFn = function keyFn() { - if (arguments.length === 1) { - return arguments[0]; - } else if (arguments.length === 0) { - return 'undefined'; - } - - var args = []; - - for (var i = 0; i < arguments.length; i++) { - args.push(arguments[i]); - } - - return args.join('$'); - }; - } +var util = __webpack_require__(1); - var memoizedFn = function memoizedFn() { - var self = this; - var args = arguments; - var ret = void 0; - var k = keyFn.apply(self, args); - var cache = memoizedFn.cache; +var fullFpsTime = 1000 / 60; // assume 60 frames per second - if (!(ret = cache[k])) { - ret = cache[k] = fn.apply(self, args); - } +module.exports = { + setupDequeueing: function setupDequeueing(opts) { + return function setupDequeueingImpl() { + var self = this; + var r = this.renderer; - return ret; - }; + if (self.dequeueingSetup) { + return; + } else { + self.dequeueingSetup = true; + } - memoizedFn.cache = {}; + var queueRedraw = util.debounce(function () { + r.redrawHint('eles', true); + r.redrawHint('drag', true); - return memoizedFn; -}; + r.redraw(); + }, opts.deqRedrawThreshold); -/***/ }), -/* 20 */ -/***/ (function(module, exports, __webpack_require__) { + var dequeue = function dequeue(willDraw, frameStartTime) { + var startTime = util.performanceNow(); + var avgRenderTime = r.averageRedrawTime; + var renderTime = r.lastRedrawTime; + var deqd = []; + var extent = r.cy.extent(); + var pixelRatio = r.getPixelRatio(); -"use strict"; + while (true) { + var now = util.performanceNow(); + var duration = now - startTime; + var frameDuration = now - frameStartTime; + if (renderTime < fullFpsTime) { + // if we're rendering faster than the ideal fps, then do dequeueing + // during all of the remaining frame time -var util = __webpack_require__(1); -var define = __webpack_require__(3); -var Collection = __webpack_require__(7); -var Core = __webpack_require__(12); -var incExts = __webpack_require__(75); -var is = __webpack_require__(0); -var Emitter = __webpack_require__(10); + var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0); -// registered extensions to cytoscape, indexed by name -var extensions = {}; - -// registered modules for extensions, indexed by name -var modules = {}; + if (frameDuration >= opts.deqFastCost * timeAvailable) { + break; + } + } else { + if (willDraw) { + if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) { + break; + } + } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) { + break; + } + } -function setExtension(type, name, registrant) { + var thisDeqd = opts.deq(self, pixelRatio, extent); - var ext = registrant; + if (thisDeqd.length > 0) { + for (var i = 0; i < thisDeqd.length; i++) { + deqd.push(thisDeqd[i]); + } + } else { + break; + } + } - var overrideErr = function overrideErr(field) { - util.error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden'); - }; + // callbacks on dequeue + if (deqd.length > 0) { + opts.onDeqd(self, deqd); - if (type === 'core') { - if (Core.prototype[name]) { - return overrideErr(name); - } else { - Core.prototype[name] = registrant; - } - } else if (type === 'collection') { - if (Collection.prototype[name]) { - return overrideErr(name); - } else { - Collection.prototype[name] = registrant; - } - } else if (type === 'layout') { - // fill in missing layout functions in the prototype + if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) { + queueRedraw(); + } + } + }; - var Layout = function Layout(options) { - this.options = options; + var priority = opts.priority || util.noop; - registrant.call(this, options); + r.beforeRender(dequeue, priority(self)); + }; + } +}; - // make sure layout has _private for use w/ std apis like .on() - if (!is.plainObject(this._private)) { - this._private = {}; - } +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { - this._private.cy = options.cy; - this._private.listeners = []; +"use strict"; - this.createEmitter(); - }; - var layoutProto = Layout.prototype = Object.create(registrant.prototype); +var is = __webpack_require__(0); +var Core = __webpack_require__(12); +var extension = __webpack_require__(94); +var Stylesheet = __webpack_require__(136); - var optLayoutFns = []; +var cytoscape = function cytoscape(options) { + // jshint ignore:line + // if no options specified, use default + if (options === undefined) { + options = {}; + } - for (var i = 0; i < optLayoutFns.length; i++) { - var fnName = optLayoutFns[i]; + // create instance + if (is.plainObject(options)) { + return new Core(options); + } - layoutProto[fnName] = layoutProto[fnName] || function () { - return this; - }; + // allow for registration of extensions + else if (is.string(options)) { + return extension.apply(extension, arguments); } +}; - // either .start() or .run() is defined, so autogen the other - if (layoutProto.start && !layoutProto.run) { - layoutProto.run = function () { - this.start();return this; - }; - } else if (!layoutProto.start && layoutProto.run) { - layoutProto.start = function () { - this.run();return this; - }; - } +// e.g. cytoscape.use( require('cytoscape-foo'), bar ) +cytoscape.use = function (ext) { + var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext - var regStop = registrant.prototype.stop; - layoutProto.stop = function () { - var opts = this.options; + args.unshift(cytoscape); // cytoscape is first arg to ext - if (opts && opts.animate) { - var anis = this.animations; + ext.apply(null, args); - if (anis) { - for (var _i = 0; _i < anis.length; _i++) { - anis[_i].stop(); - } - } - } + return this; +}; - if (regStop) { - regStop.call(this); - } else { - this.emit('layoutstop'); - } +// replaced by build system +cytoscape.version = __webpack_require__(137); - return this; - }; +// expose public apis (mostly for extensions) +cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet; - if (!layoutProto.destroy) { - layoutProto.destroy = function () { - return this; - }; - } +module.exports = cytoscape; - layoutProto.cy = function () { - return this._private.cy; - }; +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { - var getCy = function getCy(layout) { - return layout._private.cy; - }; +"use strict"; - util.assign(layoutProto, { - createEmitter: function createEmitter() { - this._private.emitter = new Emitter({ - eventFields: function eventFields(layout) { - return { - layout: layout, - cy: getCy(layout), - target: layout - }; - }, - bubble: function bubble() { - return true; - }, - parent: function parent(layout) { - return getCy(layout); - }, - context: this - }); - return this; - }, - emitter: function emitter() { - return this._private.emitter; - }, - on: function on(evt, cb) { - this.emitter().on(evt, cb);return this; - }, - one: function one(evt, cb) { - this.emitter().one(evt, cb);return this; - }, - once: function once(evt, cb) { - this.emitter().one(evt, cb);return this; - }, - removeListener: function removeListener(evt, cb) { - this.emitter().removeListener(evt, cb);return this; - }, - emit: function emit(evt, params) { - this.emitter().emit(evt, params);return this; - } - }); +var is = __webpack_require__(0); - define.eventAliasesOn(layoutProto); +module.exports = { + // get [r, g, b] from #abc or #aabbcc + hex2tuple: function hex2tuple(hex) { + if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') { + return; + } - ext = Layout; // replace with our wrapped layout - } else if (type === 'renderer' && name !== 'null' && name !== 'base') { - // user registered renderers inherit from base + var shortHex = hex.length === 4; + var r = void 0, + g = void 0, + b = void 0; + var base = 16; - var BaseRenderer = getExtension('renderer', 'base'); - var bProto = BaseRenderer.prototype; - var RegistrantRenderer = registrant; - var rProto = registrant.prototype; + if (shortHex) { + r = parseInt(hex[1] + hex[1], base); + g = parseInt(hex[2] + hex[2], base); + b = parseInt(hex[3] + hex[3], base); + } else { + r = parseInt(hex[1] + hex[2], base); + g = parseInt(hex[3] + hex[4], base); + b = parseInt(hex[5] + hex[6], base); + } - var Renderer = function Renderer() { - BaseRenderer.apply(this, arguments); - RegistrantRenderer.apply(this, arguments); - }; + return [r, g, b]; + }, - var proto = Renderer.prototype; + // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0) + hsl2tuple: function hsl2tuple(hsl) { + var ret = void 0; + var h = void 0, + s = void 0, + l = void 0, + a = void 0, + r = void 0, + g = void 0, + b = void 0; + function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + } - for (var pName in bProto) { - var pVal = bProto[pName]; - var existsInR = rProto[pName] != null; + var m = new RegExp('^' + this.regex.hsla + '$').exec(hsl); + if (m) { - if (existsInR) { - return overrideErr(pName); + // get hue + h = parseInt(m[1]); + if (h < 0) { + h = (360 - -1 * h % 360) % 360; + } else if (h > 360) { + h = h % 360; } + h /= 360; // normalise on [0, 1] - proto[pName] = pVal; // take impl from base - } - - for (var _pName in rProto) { - proto[_pName] = rProto[_pName]; // take impl from registrant - } - - bProto.clientFunctions.forEach(function (name) { - proto[name] = proto[name] || function () { - util.error('Renderer does not implement `renderer.' + name + '()` on its prototype'); - }; - }); - - ext = Renderer; - } - - return util.setMap({ - map: extensions, - keys: [type, name], - value: ext - }); -} - -function getExtension(type, name) { - return util.getMap({ - map: extensions, - keys: [type, name] - }); -} - -function setModule(type, name, moduleType, moduleName, registrant) { - return util.setMap({ - map: modules, - keys: [type, name, moduleType, moduleName], - value: registrant - }); -} - -function getModule(type, name, moduleType, moduleName) { - return util.getMap({ - map: modules, - keys: [type, name, moduleType, moduleName] - }); -} + s = parseFloat(m[2]); + if (s < 0 || s > 100) { + return; + } // saturation is [0, 100] + s = s / 100; // normalise on [0, 1] -var extension = function extension() { - // e.g. extension('renderer', 'svg') - if (arguments.length === 2) { - return getExtension.apply(null, arguments); - } + l = parseFloat(m[3]); + if (l < 0 || l > 100) { + return; + } // lightness is [0, 100] + l = l / 100; // normalise on [0, 1] - // e.g. extension('renderer', 'svg', { ... }) - else if (arguments.length === 3) { - return setExtension.apply(null, arguments); - } + a = m[4]; + if (a !== undefined) { + a = parseFloat(a); - // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse') - else if (arguments.length === 4) { - return getModule.apply(null, arguments); + if (a < 0 || a > 1) { + return; + } // alpha is [0, 1] } - // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... }) - else if (arguments.length === 5) { - return setModule.apply(null, arguments); - } else { - util.error('Invalid extension access syntax'); - } -}; - -// allows a core instance to access extensions internally -Core.prototype.extension = extension; - -// included extensions -incExts.forEach(function (group) { - group.extensions.forEach(function (ext) { - setExtension(group.type, ext.name, ext.impl); - }); -}); - -module.exports = extension; - -/***/ }), -/* 21 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var is = __webpack_require__(0); -var util = __webpack_require__(1); -var Style = __webpack_require__(18); - -// a dummy stylesheet object that doesn't need a reference to the core -// (useful for init) -var Stylesheet = function Stylesheet() { - if (!(this instanceof Stylesheet)) { - return new Stylesheet(); - } - - this.length = 0; -}; - -var sheetfn = Stylesheet.prototype; - -sheetfn.instanceString = function () { - return 'stylesheet'; -}; - -// just store the selector to be parsed later -sheetfn.selector = function (selector) { - var i = this.length++; - - this[i] = { - selector: selector, - properties: [] - }; - - return this; // chaining -}; - -// just store the property to be parsed later -sheetfn.css = function (name, value) { - var i = this.length - 1; - - if (is.string(name)) { - this[i].properties.push({ - name: name, - value: value - }); - } else if (is.plainObject(name)) { - var map = name; - - for (var j = 0; j < Style.properties.length; j++) { - var prop = Style.properties[j]; - var mapVal = map[prop.name]; - - if (mapVal === undefined) { - // also try camel case name - mapVal = map[util.dash2camel(prop.name)]; + // now, convert to rgb + // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + if (s === 0) { + r = g = b = Math.round(l * 255); // achromatic + } else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = Math.round(255 * hue2rgb(p, q, h + 1 / 3)); + g = Math.round(255 * hue2rgb(p, q, h)); + b = Math.round(255 * hue2rgb(p, q, h - 1 / 3)); } - if (mapVal !== undefined) { - var _name = prop.name; - var _value = mapVal; - - this[i].properties.push({ - name: _name, - value: _value - }); - } + ret = [r, g, b, a]; } - } - - return this; // chaining -}; - -sheetfn.style = sheetfn.css; -// generate a real style object from the dummy stylesheet -sheetfn.generateStyle = function (cy) { - var style = new Style(cy); + return ret; + }, - return this.appendToStyle(style); -}; + // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0) + rgb2tuple: function rgb2tuple(rgb) { + var ret = void 0; -// append a dummy stylesheet object on a real style object -sheetfn.appendToStyle = function (style) { - for (var i = 0; i < this.length; i++) { - var context = this[i]; - var selector = context.selector; - var props = context.properties; + var m = new RegExp('^' + this.regex.rgba + '$').exec(rgb); + if (m) { + ret = []; - style.selector(selector); // apply selector + var isPct = []; + for (var i = 1; i <= 3; i++) { + var channel = m[i]; - for (var j = 0; j < props.length; j++) { - var prop = props[j]; + if (channel[channel.length - 1] === '%') { + isPct[i] = true; + } + channel = parseFloat(channel); - style.css(prop.name, prop.value); // apply property - } - } + if (isPct[i]) { + channel = channel / 100 * 255; // normalise to [0, 255] + } - return style; -}; + if (channel < 0 || channel > 255) { + return; + } // invalid channel value -module.exports = Stylesheet; + ret.push(Math.floor(channel)); + } -/***/ }), -/* 22 */ -/***/ (function(module, exports, __webpack_require__) { + var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3]; + var allArePct = isPct[1] && isPct[2] && isPct[3]; + if (atLeastOneIsPct && !allArePct) { + return; + } // must all be percent values if one is -"use strict"; + var alpha = m[4]; + if (alpha !== undefined) { + alpha = parseFloat(alpha); + if (alpha < 0 || alpha > 1) { + return; + } // invalid alpha value -module.exports = "3.2.8"; + ret.push(alpha); + } + } -/***/ }), -/* 23 */ -/***/ (function(module, exports, __webpack_require__) { + return ret; + }, -"use strict"; + colorname2tuple: function colorname2tuple(color) { + return this.colors[color.toLowerCase()]; + }, + color2tuple: function color2tuple(color) { + return (is.array(color) ? color : null) || this.colorname2tuple(color) || this.hex2tuple(color) || this.rgb2tuple(color) || this.hsl2tuple(color); + }, -var util = __webpack_require__(1); -var is = __webpack_require__(0); -var Promise = __webpack_require__(5); + colors: { + // special colour names + transparent: [0, 0, 0, 0], // NB alpha === 0 -var Animation = function Animation(target, opts, opts2) { - var _p = this._private = util.extend({ - duration: 1000 - }, opts, opts2); - - _p.target = target; - _p.style = _p.style || _p.css; - _p.started = false; - _p.playing = false; - _p.hooked = false; - _p.applying = false; - _p.progress = 0; - _p.completes = []; - _p.frames = []; - - if (_p.complete && is.fn(_p.complete)) { - _p.completes.push(_p.complete); + // regular colours + aliceblue: [240, 248, 255], + antiquewhite: [250, 235, 215], + aqua: [0, 255, 255], + aquamarine: [127, 255, 212], + azure: [240, 255, 255], + beige: [245, 245, 220], + bisque: [255, 228, 196], + black: [0, 0, 0], + blanchedalmond: [255, 235, 205], + blue: [0, 0, 255], + blueviolet: [138, 43, 226], + brown: [165, 42, 42], + burlywood: [222, 184, 135], + cadetblue: [95, 158, 160], + chartreuse: [127, 255, 0], + chocolate: [210, 105, 30], + coral: [255, 127, 80], + cornflowerblue: [100, 149, 237], + cornsilk: [255, 248, 220], + crimson: [220, 20, 60], + cyan: [0, 255, 255], + darkblue: [0, 0, 139], + darkcyan: [0, 139, 139], + darkgoldenrod: [184, 134, 11], + darkgray: [169, 169, 169], + darkgreen: [0, 100, 0], + darkgrey: [169, 169, 169], + darkkhaki: [189, 183, 107], + darkmagenta: [139, 0, 139], + darkolivegreen: [85, 107, 47], + darkorange: [255, 140, 0], + darkorchid: [153, 50, 204], + darkred: [139, 0, 0], + darksalmon: [233, 150, 122], + darkseagreen: [143, 188, 143], + darkslateblue: [72, 61, 139], + darkslategray: [47, 79, 79], + darkslategrey: [47, 79, 79], + darkturquoise: [0, 206, 209], + darkviolet: [148, 0, 211], + deeppink: [255, 20, 147], + deepskyblue: [0, 191, 255], + dimgray: [105, 105, 105], + dimgrey: [105, 105, 105], + dodgerblue: [30, 144, 255], + firebrick: [178, 34, 34], + floralwhite: [255, 250, 240], + forestgreen: [34, 139, 34], + fuchsia: [255, 0, 255], + gainsboro: [220, 220, 220], + ghostwhite: [248, 248, 255], + gold: [255, 215, 0], + goldenrod: [218, 165, 32], + gray: [128, 128, 128], + grey: [128, 128, 128], + green: [0, 128, 0], + greenyellow: [173, 255, 47], + honeydew: [240, 255, 240], + hotpink: [255, 105, 180], + indianred: [205, 92, 92], + indigo: [75, 0, 130], + ivory: [255, 255, 240], + khaki: [240, 230, 140], + lavender: [230, 230, 250], + lavenderblush: [255, 240, 245], + lawngreen: [124, 252, 0], + lemonchiffon: [255, 250, 205], + lightblue: [173, 216, 230], + lightcoral: [240, 128, 128], + lightcyan: [224, 255, 255], + lightgoldenrodyellow: [250, 250, 210], + lightgray: [211, 211, 211], + lightgreen: [144, 238, 144], + lightgrey: [211, 211, 211], + lightpink: [255, 182, 193], + lightsalmon: [255, 160, 122], + lightseagreen: [32, 178, 170], + lightskyblue: [135, 206, 250], + lightslategray: [119, 136, 153], + lightslategrey: [119, 136, 153], + lightsteelblue: [176, 196, 222], + lightyellow: [255, 255, 224], + lime: [0, 255, 0], + limegreen: [50, 205, 50], + linen: [250, 240, 230], + magenta: [255, 0, 255], + maroon: [128, 0, 0], + mediumaquamarine: [102, 205, 170], + mediumblue: [0, 0, 205], + mediumorchid: [186, 85, 211], + mediumpurple: [147, 112, 219], + mediumseagreen: [60, 179, 113], + mediumslateblue: [123, 104, 238], + mediumspringgreen: [0, 250, 154], + mediumturquoise: [72, 209, 204], + mediumvioletred: [199, 21, 133], + midnightblue: [25, 25, 112], + mintcream: [245, 255, 250], + mistyrose: [255, 228, 225], + moccasin: [255, 228, 181], + navajowhite: [255, 222, 173], + navy: [0, 0, 128], + oldlace: [253, 245, 230], + olive: [128, 128, 0], + olivedrab: [107, 142, 35], + orange: [255, 165, 0], + orangered: [255, 69, 0], + orchid: [218, 112, 214], + palegoldenrod: [238, 232, 170], + palegreen: [152, 251, 152], + paleturquoise: [175, 238, 238], + palevioletred: [219, 112, 147], + papayawhip: [255, 239, 213], + peachpuff: [255, 218, 185], + peru: [205, 133, 63], + pink: [255, 192, 203], + plum: [221, 160, 221], + powderblue: [176, 224, 230], + purple: [128, 0, 128], + red: [255, 0, 0], + rosybrown: [188, 143, 143], + royalblue: [65, 105, 225], + saddlebrown: [139, 69, 19], + salmon: [250, 128, 114], + sandybrown: [244, 164, 96], + seagreen: [46, 139, 87], + seashell: [255, 245, 238], + sienna: [160, 82, 45], + silver: [192, 192, 192], + skyblue: [135, 206, 235], + slateblue: [106, 90, 205], + slategray: [112, 128, 144], + slategrey: [112, 128, 144], + snow: [255, 250, 250], + springgreen: [0, 255, 127], + steelblue: [70, 130, 180], + tan: [210, 180, 140], + teal: [0, 128, 128], + thistle: [216, 191, 216], + tomato: [255, 99, 71], + turquoise: [64, 224, 208], + violet: [238, 130, 238], + wheat: [245, 222, 179], + white: [255, 255, 255], + whitesmoke: [245, 245, 245], + yellow: [255, 255, 0], + yellowgreen: [154, 205, 50] } - - // for future timeline/animations impl - this.length = 1; - this[0] = this; }; -var anifn = Animation.prototype; - -util.extend(anifn, { +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { - instanceString: function instanceString() { - return 'animation'; - }, +"use strict"; - hook: function hook() { - var _p = this._private; - if (!_p.hooked) { - // add to target's animation queue - var q = void 0; - var tAni = _p.target._private.animation; - if (_p.queue) { - q = tAni.queue; - } else { - q = tAni.current; - } - q.push(this); +var is = __webpack_require__(0); - // add to the animation loop pool - if (is.elementOrCollection(_p.target)) { - _p.target.cy().addToAnimationPool(_p.target); - } +module.exports = { + // has anything been set in the map + mapEmpty: function mapEmpty(map) { + var empty = true; - _p.hooked = true; + if (map != null) { + return Object.keys(map).length === 0; } - return this; + return empty; }, - play: function play() { - var _p = this._private; + // pushes to the array at the end of a map (map may not be built) + pushMap: function pushMap(options) { + var array = this.getMap(options); - // autorewind - if (_p.progress === 1) { - _p.progress = 0; + if (array == null) { + // if empty, put initial array + this.setMap(this.extend({}, options, { + value: [options.value] + })); + } else { + array.push(options.value); } - - _p.playing = true; - _p.started = false; // needs to be started by animation loop - _p.stopped = false; - - this.hook(); - - // the animation loop will start the animation... - - return this; }, - playing: function playing() { - return this._private.playing; - }, + // sets the value in a map (map may not be built) + setMap: function setMap(options) { + var obj = options.map; + var key = void 0; + var keys = options.keys; + var l = keys.length; - apply: function apply() { - var _p = this._private; + for (var i = 0; i < l; i++) { + var _key = keys[i]; - _p.applying = true; - _p.started = false; // needs to be started by animation loop - _p.stopped = false; + if (is.plainObject(_key)) { + this.error('Tried to set map with object key'); + } - this.hook(); + if (i < keys.length - 1) { - // the animation loop will apply the animation at this progress + // extend the map if necessary + if (obj[_key] == null) { + obj[_key] = {}; + } - return this; - }, - - applying: function applying() { - return this._private.applying; - }, - - pause: function pause() { - var _p = this._private; - - _p.playing = false; - _p.started = false; - - return this; - }, - - stop: function stop() { - var _p = this._private; - - _p.playing = false; - _p.started = false; - _p.stopped = true; // to be removed from animation queues - - return this; - }, - - rewind: function rewind() { - return this.progress(0); - }, - - fastforward: function fastforward() { - return this.progress(1); - }, - - time: function time(t) { - var _p = this._private; - - if (t === undefined) { - return _p.progress * _p.duration; - } else { - return this.progress(t / _p.duration); + obj = obj[_key]; + } else { + // set the value + obj[_key] = options.value; + } } }, - progress: function progress(p) { - var _p = this._private; - var wasPlaying = _p.playing; + // gets the value in a map even if it's not built in places + getMap: function getMap(options) { + var obj = options.map; + var keys = options.keys; + var l = keys.length; - if (p === undefined) { - return _p.progress; - } else { - if (wasPlaying) { - this.pause(); + for (var i = 0; i < l; i++) { + var key = keys[i]; + + if (is.plainObject(key)) { + this.error('Tried to get map with object key'); } - _p.progress = p; - _p.started = false; + obj = obj[key]; - if (wasPlaying) { - this.play(); + if (obj == null) { + return obj; } } - return this; - }, - - completed: function completed() { - return this._private.progress === 1; + return obj; }, - reverse: function reverse() { - var _p = this._private; - var wasPlaying = _p.playing; - - if (wasPlaying) { - this.pause(); - } - - _p.progress = 1 - _p.progress; - _p.started = false; + // deletes the entry in the map + deleteMap: function deleteMap(options) { + var obj = options.map; + var keys = options.keys; + var l = keys.length; + var keepChildren = options.keepChildren; - var swap = function swap(a, b) { - var _pa = _p[a]; + for (var i = 0; i < l; i++) { + var key = keys[i]; - if (_pa == null) { - return; + if (is.plainObject(key)) { + this.error('Tried to delete map with object key'); } - _p[a] = _p[b]; - _p[b] = _pa; - }; + var lastKey = i === options.keys.length - 1; + if (lastKey) { - swap('zoom', 'startZoom'); - swap('pan', 'startPan'); - swap('position', 'startPosition'); + if (keepChildren) { + // then only delete child fields not in keepChildren + var children = Object.keys(obj); - // swap styles - if (_p.style) { - for (var i = 0; i < _p.style.length; i++) { - var prop = _p.style[i]; - var name = prop.name; - var startStyleProp = _p.startStyle[name]; + for (var j = 0; j < children.length; j++) { + var child = children[j]; - _p.startStyle[name] = prop; - _p.style[i] = startStyleProp; + if (!keepChildren[child]) { + obj[child] = undefined; + } + } + } else { + obj[key] = undefined; + } + } else { + obj = obj[key]; } } + } +}; - if (wasPlaying) { - this.play(); - } - - return this; - }, +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { - promise: function promise(type) { - var _p = this._private; +"use strict"; - var arr = void 0; - switch (type) { - case 'frame': - arr = _p.frames; - break; - default: - case 'complete': - case 'completed': - arr = _p.completes; - } +var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))'; - return new Promise(function (resolve, reject) { - arr.push(function () { - resolve(); - }); - }); - } +var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)'; +var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)'; -}); +var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)'; +var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)'; -anifn.complete = anifn.completed; +var hex3 = '\\#[0-9a-fA-F]{3}'; +var hex6 = '\\#[0-9a-fA-F]{6}'; -module.exports = Animation; +module.exports = { + regex: { + number: number, + rgba: rgba, + rgbaNoBackRefs: rgbaNoBackRefs, + hsla: hsla, + hslaNoBackRefs: hslaNoBackRefs, + hex3: hex3, + hex6: hex6 + } +}; /***/ }), /* 24 */ @@ -4972,617 +4862,549 @@ module.exports = Animation; "use strict"; +var memoize = __webpack_require__(13); var is = __webpack_require__(0); -var elesfn = { +module.exports = { - // Implemented from pseudocode from wikipedia - aStar: function aStar(options) { - var eles = this; + camel2dash: memoize(function (str) { + return str.replace(/([A-Z])/g, function (v) { + return '-' + v.toLowerCase(); + }); + }), - options = options || {}; + dash2camel: memoize(function (str) { + return str.replace(/(-\w)/g, function (v) { + return v[1].toUpperCase(); + }); + }), - // Reconstructs the path from Start to End, acumulating the result in pathAcum - var reconstructPath = function reconstructPath(start, end, cameFromMap, pathAcum) { - // Base case - if (start == end) { - pathAcum.unshift(cy.getElementById(end)); - return pathAcum; - } + prependCamel: memoize(function (prefix, str) { + return prefix + str[0].toUpperCase() + str.substring(1); + }, function (prefix, str) { + return prefix + '$' + str; + }), - if (end in cameFromMap) { - // We know which node is before the last one - var previous = cameFromMap[end]; - var previousEdge = cameFromEdge[end]; + capitalize: function capitalize(str) { + if (is.emptyString(str)) { + return str; + } - pathAcum.unshift(cy.getElementById(previousEdge)); - pathAcum.unshift(cy.getElementById(end)); + return str.charAt(0).toUpperCase() + str.substring(1); + } - return reconstructPath(start, previous, cameFromMap, pathAcum); - } +}; - // We should not reach here! - return undefined; - }; +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { - // Returns the index of the element in openSet which has minimum fScore - var findMin = function findMin(openSet, fScore) { - if (openSet.length === 0) { - // Should never be the case - return undefined; - } - var minPos = 0; - var tempScore = fScore[openSet[0]]; - for (var i = 1; i < openSet.length; i++) { - var s = fScore[openSet[i]]; - if (s < tempScore) { - tempScore = s; - minPos = i; - } - } - return minPos; - }; +"use strict"; - var cy = this._private.cy; - // root - mandatory! - if (options != null && options.root != null) { - var source = is.string(options.root) ? - // use it as a selector, e.g. "#rootID - this.filter(options.root)[0] : options.root[0]; - } else { - return undefined; - } +var window = __webpack_require__(3); +var performance = window ? window.performance : null; - // goal - mandatory! - if (options.goal != null) { - var target = is.string(options.goal) ? - // use it as a selector, e.g. "#goalID - this.filter(options.goal)[0] : options.goal[0]; - } else { - return undefined; - } +var util = {}; - // Heuristic function - optional - if (options.heuristic != null && is.fn(options.heuristic)) { - var heuristic = options.heuristic; - } else { - var heuristic = function heuristic() { - return 0; - }; // use constant if unspecified - } +var pnow = performance && performance.now ? function () { + return performance.now(); +} : function () { + return Date.now(); +}; - // Weight function - optional - if (options.weight != null && is.fn(options.weight)) { - var weightFn = options.weight; - } else { - // If not specified, assume each edge has equal weight (1) - var weightFn = function weightFn(e) { - return 1; +var raf = function () { + if (window) { + if (window.requestAnimationFrame) { + return function (fn) { + window.requestAnimationFrame(fn); + }; + } else if (window.mozRequestAnimationFrame) { + return function (fn) { + window.mozRequestAnimationFrame(fn); + }; + } else if (window.webkitRequestAnimationFrame) { + return function (fn) { + window.webkitRequestAnimationFrame(fn); + }; + } else if (window.msRequestAnimationFrame) { + return function (fn) { + window.msRequestAnimationFrame(fn); }; } + } - // directed - optional - if (options.directed != null) { - var directed = options.directed; - } else { - var directed = false; + return function (fn) { + if (fn) { + setTimeout(function () { + fn(pnow()); + }, 1000 / 60); } + }; +}(); - var sid = source.id(); - var tid = target.id(); +util.requestAnimationFrame = function (fn) { + raf(fn); +}; - var closedSet = []; - var openSet = [sid]; - var cameFrom = {}; - var cameFromEdge = {}; - var gScore = {}; - var fScore = {}; +util.performanceNow = pnow; - gScore[sid] = 0; - fScore[sid] = heuristic(source); +util.debounce = __webpack_require__(26); - // Counter - var steps = 0; +util.now = function () { + return Date.now(); +}; - // Main loop - while (openSet.length > 0) { - var minPos = findMin(openSet, fScore); - var cMin = cy.getElementById(openSet[minPos]); - var cMinId = cMin.id(); - steps++; +module.exports = util; - // If we've found our goal, then we are done - if (cMinId == tid) { - var rPath = reconstructPath(sid, tid, cameFrom, []); +/***/ }), +/* 26 */ +/***/ (function(module, exports) { - return { - found: true, - distance: gScore[cMinId], - path: eles.spawn(rPath), - steps: steps - }; - } +module.exports = __WEBPACK_EXTERNAL_MODULE_26__; - // Add cMin to processed nodes - closedSet.push(cMinId); - // Remove cMin from boundary nodes - openSet.splice(minPos, 1); +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { - // Update scores for neighbors of cMin - // Take into account if graph is directed or not - var vwEdges = cMin._private.edges; +"use strict"; - for (var i = 0; i < vwEdges.length; i++) { - var e = vwEdges[i]; - // edge must be in set of calling eles - if (!this.hasElementWithId(e.id())) { - continue; - } +function ascending(a, b) { + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } +} - // cMin must be the source of edge if directed - if (directed && e.data('source') !== cMinId) { - continue; - } +function descending(a, b) { + return -1 * ascending(a, b); +} - var wSrc = e.source(); - var wTgt = e.target(); +module.exports = { + sort: { + ascending: ascending, + descending: descending + } +}; - var w = wSrc.id() !== cMinId ? wSrc : wTgt; - var wid = w.id(); +/***/ }), +/* 28 */ +/***/ (function(module, exports, __webpack_require__) { - // node must be in set of calling eles - if (!this.hasElementWithId(wid)) { - continue; - } +"use strict"; - // if node is in closedSet, ignore it - if (closedSet.indexOf(wid) != -1) { - continue; - } - // New tentative score for node w - var tempScore = gScore[cMinId] + weightFn(e); +function ObjectMap() { + this._obj = {}; +} - // Update gScore for node w if: - // w not present in openSet - // OR - // tentative gScore is less than previous value +var p = ObjectMap.prototype; - // w not in openSet - if (openSet.indexOf(wid) == -1) { - gScore[wid] = tempScore; - fScore[wid] = tempScore + heuristic(w); - openSet.push(wid); // Add node to openSet - cameFrom[wid] = cMinId; - cameFromEdge[wid] = e.id(); - continue; - } - // w already in openSet, but with greater gScore - if (tempScore < gScore[wid]) { - gScore[wid] = tempScore; - fScore[wid] = tempScore + heuristic(w); - cameFrom[wid] = cMinId; - } - } // End of neighbors update - } // End of main loop +p.set = function (key, val) { + this._obj[key] = val; +}; - // If we've reached here, then we've not reached our goal - return { - found: false, - distance: undefined, - path: undefined, - steps: steps - }; - } +p.delete = function (key) { + this._obj[key] = null; +}; -}; // elesfn +p.has = function (key) { + return this._obj[key] != null; +}; +p.get = function (key) { + return this._obj[key]; +}; -module.exports = elesfn; +// TODO use the stdlib Map in future... +// module.exports = typeof Map !== 'undefined' ? Map : ObjectMap; +module.exports = ObjectMap; /***/ }), -/* 25 */ +/* 29 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var is = __webpack_require__(0); var util = __webpack_require__(1); -var elesfn = { +var elesfn = {}; - // Implemented from pseudocode from wikipedia - bellmanFord: function bellmanFord(options) { - var eles = this; +[__webpack_require__(30), __webpack_require__(31), __webpack_require__(33), __webpack_require__(34), __webpack_require__(35), __webpack_require__(36), __webpack_require__(37), __webpack_require__(38), __webpack_require__(39), __webpack_require__(40), __webpack_require__(41)].forEach(function (props) { + util.extend(elesfn, props); +}); - options = options || {}; +module.exports = elesfn; - // Weight function - optional - if (options.weight != null && is.fn(options.weight)) { - var weightFn = options.weight; - } else { - // If not specified, assume each edge has equal weight (1) - var weightFn = function weightFn(e) { - return 1; - }; - } +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { - // directed - optional - if (options.directed != null) { - var directed = options.directed; - } else { - var directed = false; - } +"use strict"; - // root - mandatory! - if (options.root != null) { - if (is.string(options.root)) { - // use it as a selector, e.g. "#rootID - var source = this.filter(options.root)[0]; - } else { - var source = options.root[0]; - } - } else { - return undefined; - } - var cy = this._private.cy; - var edges = this.edges().stdFilter(function (e) { - return !e.isLoop(); - }); - var nodes = this.nodes(); - var numNodes = nodes.length; +var is = __webpack_require__(0); - // mapping: node id -> position in nodes array - var id2position = {}; - for (var i = 0; i < numNodes; i++) { - id2position[nodes[i].id()] = i; +var defineSearch = function defineSearch(params) { + params = { + bfs: params.bfs || !params.dfs, + dfs: params.dfs || !params.bfs + }; + + // from pseudocode on wikipedia + return function searchFn(roots, fn, directed) { + var options; + if (is.plainObject(roots) && !is.elementOrCollection(roots)) { + options = roots; + roots = options.roots || options.root; + fn = options.visit; + directed = options.directed; } - // Initializations - var cost = []; - var predecessor = []; - var predEdge = []; + directed = arguments.length === 2 && !is.fn(fn) ? fn : directed; + fn = is.fn(fn) ? fn : function () {}; - for (var i = 0; i < numNodes; i++) { - if (nodes[i].id() === source.id()) { - cost[i] = 0; - } else { - cost[i] = Infinity; - } - predecessor[i] = undefined; - } + var cy = this._private.cy; + var v = roots = is.string(roots) ? this.filter(roots) : roots; + var Q = []; + var connectedNodes = []; + var connectedBy = {}; + var id2depth = {}; + var V = {}; + var j = 0; + var found; + var nodes = this.nodes(); + var edges = this.edges(); - // Edges relaxation - var flag = false; - for (var i = 1; i < numNodes; i++) { - flag = false; - for (var e = 0; e < edges.length; e++) { - var sourceIndex = id2position[edges[e].source().id()]; - var targetIndex = id2position[edges[e].target().id()]; - var weight = weightFn(edges[e]); + // enqueue v + for (var i = 0; i < v.length; i++) { + if (v[i].isNode()) { + Q.unshift(v[i]); - var temp = cost[sourceIndex] + weight; - if (temp < cost[targetIndex]) { - cost[targetIndex] = temp; - predecessor[targetIndex] = sourceIndex; - predEdge[targetIndex] = edges[e]; - flag = true; - } + if (params.bfs) { + V[v[i].id()] = true; - // If undirected graph, we need to take into account the 'reverse' edge - if (!directed) { - var temp = cost[targetIndex] + weight; - if (temp < cost[sourceIndex]) { - cost[sourceIndex] = temp; - predecessor[sourceIndex] = targetIndex; - predEdge[sourceIndex] = edges[e]; - flag = true; - } + connectedNodes.push(v[i]); } - } - if (!flag) { - break; + id2depth[v[i].id()] = 0; } } - if (flag) { - // Check for negative weight cycles - for (var e = 0; e < edges.length; e++) { - var sourceIndex = id2position[edges[e].source().id()]; - var targetIndex = id2position[edges[e].target().id()]; - var weight = weightFn(edges[e]); + while (Q.length !== 0) { + var v = params.bfs ? Q.shift() : Q.pop(); - if (cost[sourceIndex] + weight < cost[targetIndex]) { - util.error('Graph contains a negative weight cycle for Bellman-Ford'); - return { pathTo: undefined, - distanceTo: undefined, - hasNegativeWeightCycle: true }; + if (params.dfs) { + if (V[v.id()]) { + continue; } + + V[v.id()] = true; + + connectedNodes.push(v); } - } - // Build result object - var position2id = []; - for (var i = 0; i < numNodes; i++) { - position2id.push(nodes[i].id()); - } + var depth = id2depth[v.id()]; + var prevEdge = connectedBy[v.id()]; + var prevNode = prevEdge == null ? undefined : prevEdge.connectedNodes().not(v)[0]; + var ret; - var res = { - distanceTo: function distanceTo(to) { - if (is.string(to)) { - // to is a selector string - var toId = cy.filter(to)[0].id(); - } else { - // to is a node - var toId = to.id(); - } + ret = fn(v, prevEdge, prevNode, j++, depth); - return cost[id2position[toId]]; - }, + if (ret === true) { + found = v; + break; + } - pathTo: function pathTo(to) { + if (ret === false) { + break; + } - var reconstructPathAux = function reconstructPathAux(predecessor, fromPos, toPos, position2id, acumPath, predEdge) { - for (;;) { - // Add toId to path - acumPath.push(cy.getElementById(position2id[toPos])); - acumPath.push(predEdge[toPos]); + var vwEdges = v.connectedEdges(directed ? function (ele) { + return ele.data('source') === v.id(); + } : undefined).intersect(edges); + for (var i = 0; i < vwEdges.length; i++) { + var e = vwEdges[i]; + var w = e.connectedNodes(function (n) { + return n.id() !== v.id(); + }).intersect(nodes); - if (fromPos === toPos) { - // reached starting node - return acumPath; - } + if (w.length !== 0 && !V[w.id()]) { + w = w[0]; - // If no path exists, discart acumulated path and return undefined - var predPos = predecessor[toPos]; - if (typeof predPos === 'undefined') { - return undefined; - } + Q.push(w); - toPos = predPos; + if (params.bfs) { + V[w.id()] = true; + + connectedNodes.push(w); } - }; - if (is.string(to)) { - // to is a selector string - var toId = cy.filter(to)[0].id(); - } else { - // to is a node - var toId = to.id(); + connectedBy[w.id()] = e; + + id2depth[w.id()] = id2depth[v.id()] + 1; } - var path = []; + } + } - // This returns a reversed path - var res = reconstructPathAux(predecessor, id2position[source.id()], id2position[toId], position2id, path, predEdge); + var connectedEles = []; - // Get it in the correct order and return it - if (res != null) { - res.reverse(); - } + for (var i = 0; i < connectedNodes.length; i++) { + var node = connectedNodes[i]; + var edge = connectedBy[node.id()]; - return eles.spawn(res); - }, + if (edge) { + connectedEles.push(edge); + } - hasNegativeWeightCycle: false + connectedEles.push(node); + } + + return { + path: cy.collection(connectedEles, { unique: true }), + found: cy.collection(found) }; + }; +}; - return res; - } // bellmanFord +// search, spanning trees, etc +var elesfn = { + breadthFirstSearch: defineSearch({ bfs: true }), + depthFirstSearch: defineSearch({ dfs: true }) +}; -}; // elesfn +// nice, short mathemathical alias +elesfn.bfs = elesfn.breadthFirstSearch; +elesfn.dfs = elesfn.depthFirstSearch; module.exports = elesfn; /***/ }), -/* 26 */ +/* 31 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var is = __webpack_require__(0); -var Heap = __webpack_require__(8); +var Heap = __webpack_require__(9); var elesfn = { - // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes - betweennessCentrality: function betweennessCentrality(options) { - options = options || {}; - - // Weight - optional - var weighted, weightFn; - if (is.fn(options.weight)) { + dijkstra: function dijkstra(root, weightFn, directed) { + var options; + if (is.plainObject(root) && !is.elementOrCollection(root)) { + options = root; + root = options.root; weightFn = options.weight; - weighted = true; - } else { - weighted = false; + directed = options.directed; } - // Directed - default false - var directed = options.directed != null ? options.directed : false; - var cy = this._private.cy; + weightFn = is.fn(weightFn) ? weightFn : function () { + return 1; + }; // if not specified, assume each edge has equal weight (1) - // starting - var V = this.nodes(); - var A = {}; - var _C = {}; - var max = 0; - var C = { - set: function set(key, val) { - _C[key] = val; + var source = is.string(root) ? this.filter(root)[0] : root[0]; + var dist = {}; + var prev = {}; + var knownDist = {}; - if (val > max) { - max = val; - } - }, + var edges = this.edges().filter(function (ele) { + return !ele.isLoop(); + }); + var nodes = this.nodes(); - get: function get(key) { - return _C[key]; - } + var getDist = function getDist(node) { + return dist[node.id()]; }; - // A contains the neighborhoods of every node - for (var i = 0; i < V.length; i++) { - var v = V[i]; - var vid = v.id(); + var setDist = function setDist(node, d) { + dist[node.id()] = d; - if (directed) { - A[vid] = v.outgoers().nodes(); // get outgoers of every node - } else { - A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node - } + Q.updateItem(node); + }; - C.set(vid, 0); + var Q = new Heap(function (a, b) { + return getDist(a) - getDist(b); + }); + + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + + dist[node.id()] = node.same(source) ? 0 : Infinity; + Q.push(node); } - for (var s = 0; s < V.length; s++) { - var sid = V[s].id(); - var S = []; // stack - var P = {}; - var g = {}; - var d = {}; - var Q = new Heap(function (a, b) { - return d[a] - d[b]; - }); // queue + var distBetween = function distBetween(u, v) { + var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges); + var smallestDistance = Infinity; + var smallestEdge; - // init dictionaries - for (var i = 0; i < V.length; i++) { - var vid = V[i].id(); + for (var i = 0; i < uvs.length; i++) { + var edge = uvs[i]; + var weight = weightFn(edge); - P[vid] = []; - g[vid] = 0; - d[vid] = Infinity; + if (weight < smallestDistance || !smallestEdge) { + smallestDistance = weight; + smallestEdge = edge; + } } - g[sid] = 1; // sigma - d[sid] = 0; // distance to s + return { + edge: smallestEdge, + dist: smallestDistance + }; + }; - Q.push(sid); + while (Q.size() > 0) { + var u = Q.pop(); + var smalletsDist = getDist(u); + var uid = u.id(); - while (!Q.empty()) { - var v = Q.pop(); + knownDist[uid] = smalletsDist; - S.push(v); + if (smalletsDist === Infinity) { + continue; + } - if (weighted) { - for (var j = 0; j < A[v].length; j++) { - var w = A[v][j]; - var vEle = cy.getElementById(v); + var neighbors = u.neighborhood().intersect(nodes); + for (var i = 0; i < neighbors.length; i++) { + var v = neighbors[i]; + var vid = v.id(); + var vDist = distBetween(u, v); - var edge; - if (vEle.edgesTo(w).length > 0) { - edge = vEle.edgesTo(w)[0]; - } else { - edge = w.edgesTo(vEle)[0]; - } + var alt = smalletsDist + vDist.dist; - var edgeWeight = weightFn(edge); + if (alt < getDist(v)) { + setDist(v, alt); - w = w.id(); + prev[vid] = { + node: u, + edge: vDist.edge + }; + } + } // for + } // while - if (d[w] > d[v] + edgeWeight) { - d[w] = d[v] + edgeWeight; + return { + distanceTo: function distanceTo(node) { + var target = is.string(node) ? nodes.filter(node)[0] : node[0]; - if (Q.nodes.indexOf(w) < 0) { - //if w is not in Q - Q.push(w); - } else { - // update position if w is in Q - Q.updateItem(w); - } + return knownDist[target.id()]; + }, - g[w] = 0; - P[w] = []; - } + pathTo: function pathTo(node) { + var target = is.string(node) ? nodes.filter(node)[0] : node[0]; + var S = []; + var u = target; - if (d[w] == d[v] + edgeWeight) { - g[w] = g[w] + g[v]; - P[w].push(v); - } - } - } else { - for (var j = 0; j < A[v].length; j++) { - var w = A[v][j].id(); + if (target.length > 0) { + S.unshift(target); - if (d[w] == Infinity) { - Q.push(w); + while (prev[u.id()]) { + var p = prev[u.id()]; - d[w] = d[v] + 1; - } + S.unshift(p.edge); + S.unshift(p.node); - if (d[w] == d[v] + 1) { - g[w] = g[w] + g[v]; - P[w].push(v); - } + u = p.node; } } - } - var e = {}; - for (var i = 0; i < V.length; i++) { - e[V[i].id()] = 0; + return cy.collection(S); } + }; + } +}; - while (S.length > 0) { - var w = S.pop(); +module.exports = elesfn; - for (var j = 0; j < P[w].length; j++) { - var v = P[w][j]; +/***/ }), +/* 32 */ +/***/ (function(module, exports) { - e[v] = e[v] + g[v] / g[w] * (1 + e[w]); +module.exports = __WEBPACK_EXTERNAL_MODULE_32__; - if (w != V[s].id()) { - C.set(w, C.get(w) + e[w]); - } +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var is = __webpack_require__(0); + +// search, spanning trees, etc +var elesfn = { + + // kruskal's algorithm (finds min spanning tree, assuming undirected graph) + // implemented from pseudocode from wikipedia + kruskal: function kruskal(weightFn) { + var cy = this.cy(); + + weightFn = is.fn(weightFn) ? weightFn : function () { + return 1; + }; // if not specified, assume each edge has equal weight (1) + + function findSet(ele) { + for (var i = 0; i < forest.length; i++) { + var eles = forest[i]; + + if (eles.anySame(ele)) { + return { + eles: eles, + index: i + }; } } } - var ret = { - betweenness: function betweenness(node) { - if (is.string(node)) { - var node = cy.filter(node).id(); - } else { - var node = node.id(); - } - - return C.get(node); - }, + var A = cy.collection(cy, []); + var forest = []; + var nodes = this.nodes(); - betweennessNormalized: function betweennessNormalized(node) { - if (max == 0) return 0; + for (var i = 0; i < nodes.length; i++) { + forest.push(nodes[i].collection()); + } - if (is.string(node)) { - var node = cy.filter(node).id(); - } else { - var node = node.id(); - } + var edges = this.edges(); + var S = edges.toArray().sort(function (a, b) { + var weightA = weightFn(a); + var weightB = weightFn(b); - return C.get(node) / max; - } - }; + return weightA - weightB; + }); - // alias - ret.betweennessNormalised = ret.betweennessNormalized; + for (var i = 0; i < S.length; i++) { + var edge = S[i]; + var u = edge.source()[0]; + var v = edge.target()[0]; + var setU = findSet(u); + var setV = findSet(v); - return ret; - } // betweennessCentrality + if (setU.index !== setV.index) { + A = A.add(edge); -}; // elesfn + // combine forests for u and v + forest[setU.index] = setU.eles.add(setV.eles); + forest.splice(setV.index, 1); + } + } -// nice, short mathemathical alias -elesfn.bc = elesfn.betweennessCentrality; + return nodes.add(A); + } +}; module.exports = elesfn; /***/ }), -/* 27 */ +/* 34 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -5590,142 +5412,216 @@ module.exports = elesfn; var is = __webpack_require__(0); -var defineSearch = function defineSearch(params) { - params = { - bfs: params.bfs || !params.dfs, - dfs: params.dfs || !params.bfs - }; +var elesfn = { - // from pseudocode on wikipedia - return function searchFn(roots, fn, directed) { - var options; - if (is.plainObject(roots) && !is.elementOrCollection(roots)) { - options = roots; - roots = options.roots || options.root; - fn = options.visit; - directed = options.directed; - } + // Implemented from pseudocode from wikipedia + aStar: function aStar(options) { + var eles = this; - directed = arguments.length === 2 && !is.fn(fn) ? fn : directed; - fn = is.fn(fn) ? fn : function () {}; + options = options || {}; - var cy = this._private.cy; - var v = roots = is.string(roots) ? this.filter(roots) : roots; - var Q = []; - var connectedNodes = []; - var connectedBy = {}; - var id2depth = {}; - var V = {}; - var j = 0; - var found; - var nodes = this.nodes(); - var edges = this.edges(); + // Reconstructs the path from Start to End, acumulating the result in pathAcum + var reconstructPath = function reconstructPath(start, end, cameFromMap, pathAcum) { + // Base case + if (start == end) { + pathAcum.unshift(cy.getElementById(end)); + return pathAcum; + } - // enqueue v - for (var i = 0; i < v.length; i++) { - if (v[i].isNode()) { - Q.unshift(v[i]); + if (end in cameFromMap) { + // We know which node is before the last one + var previous = cameFromMap[end]; + var previousEdge = cameFromEdge[end]; - if (params.bfs) { - V[v[i].id()] = true; + pathAcum.unshift(cy.getElementById(previousEdge)); + pathAcum.unshift(cy.getElementById(end)); - connectedNodes.push(v[i]); - } + return reconstructPath(start, previous, cameFromMap, pathAcum); + } - id2depth[v[i].id()] = 0; + // We should not reach here! + return undefined; + }; + + // Returns the index of the element in openSet which has minimum fScore + var findMin = function findMin(openSet, fScore) { + if (openSet.length === 0) { + // Should never be the case + return undefined; + } + var minPos = 0; + var tempScore = fScore[openSet[0]]; + for (var i = 1; i < openSet.length; i++) { + var s = fScore[openSet[i]]; + if (s < tempScore) { + tempScore = s; + minPos = i; + } } + return minPos; + }; + + var cy = this._private.cy; + + // root - mandatory! + if (options != null && options.root != null) { + var source = is.string(options.root) ? + // use it as a selector, e.g. "#rootID + this.filter(options.root)[0] : options.root[0]; + } else { + return undefined; } - while (Q.length !== 0) { - var v = params.bfs ? Q.shift() : Q.pop(); + // goal - mandatory! + if (options.goal != null) { + var target = is.string(options.goal) ? + // use it as a selector, e.g. "#goalID + this.filter(options.goal)[0] : options.goal[0]; + } else { + return undefined; + } - if (params.dfs) { - if (V[v.id()]) { - continue; - } + // Heuristic function - optional + if (options.heuristic != null && is.fn(options.heuristic)) { + var heuristic = options.heuristic; + } else { + var heuristic = function heuristic() { + return 0; + }; // use constant if unspecified + } - V[v.id()] = true; + // Weight function - optional + if (options.weight != null && is.fn(options.weight)) { + var weightFn = options.weight; + } else { + // If not specified, assume each edge has equal weight (1) + var weightFn = function weightFn(e) { + return 1; + }; + } - connectedNodes.push(v); - } + // directed - optional + if (options.directed != null) { + var directed = options.directed; + } else { + var directed = false; + } - var depth = id2depth[v.id()]; - var prevEdge = connectedBy[v.id()]; - var prevNode = prevEdge == null ? undefined : prevEdge.connectedNodes().not(v)[0]; - var ret; + var sid = source.id(); + var tid = target.id(); - ret = fn(v, prevEdge, prevNode, j++, depth); + var closedSet = []; + var openSet = [sid]; + var cameFrom = {}; + var cameFromEdge = {}; + var gScore = {}; + var fScore = {}; - if (ret === true) { - found = v; - break; - } + gScore[sid] = 0; + fScore[sid] = heuristic(source); - if (ret === false) { - break; + // Counter + var steps = 0; + + // Main loop + while (openSet.length > 0) { + var minPos = findMin(openSet, fScore); + var cMin = cy.getElementById(openSet[minPos]); + var cMinId = cMin.id(); + steps++; + + // If we've found our goal, then we are done + if (cMinId == tid) { + var rPath = reconstructPath(sid, tid, cameFrom, []); + + return { + found: true, + distance: gScore[cMinId], + path: eles.spawn(rPath), + steps: steps + }; } - var vwEdges = v.connectedEdges(directed ? function (ele) { - return ele.data('source') === v.id(); - } : undefined).intersect(edges); + // Add cMin to processed nodes + closedSet.push(cMinId); + // Remove cMin from boundary nodes + openSet.splice(minPos, 1); + + // Update scores for neighbors of cMin + // Take into account if graph is directed or not + var vwEdges = cMin._private.edges; + for (var i = 0; i < vwEdges.length; i++) { var e = vwEdges[i]; - var w = e.connectedNodes(function (n) { - return n.id() !== v.id(); - }).intersect(nodes); - - if (w.length !== 0 && !V[w.id()]) { - w = w[0]; - Q.push(w); + // edge must be in set of calling eles + if (!this.hasElementWithId(e.id())) { + continue; + } - if (params.bfs) { - V[w.id()] = true; + // cMin must be the source of edge if directed + if (directed && e.data('source') !== cMinId) { + continue; + } - connectedNodes.push(w); - } + var wSrc = e.source(); + var wTgt = e.target(); - connectedBy[w.id()] = e; + var w = wSrc.id() !== cMinId ? wSrc : wTgt; + var wid = w.id(); - id2depth[w.id()] = id2depth[v.id()] + 1; + // node must be in set of calling eles + if (!this.hasElementWithId(wid)) { + continue; } - } - } - var connectedEles = []; + // if node is in closedSet, ignore it + if (closedSet.indexOf(wid) != -1) { + continue; + } - for (var i = 0; i < connectedNodes.length; i++) { - var node = connectedNodes[i]; - var edge = connectedBy[node.id()]; + // New tentative score for node w + var tempScore = gScore[cMinId] + weightFn(e); - if (edge) { - connectedEles.push(edge); - } + // Update gScore for node w if: + // w not present in openSet + // OR + // tentative gScore is less than previous value - connectedEles.push(node); - } + // w not in openSet + if (openSet.indexOf(wid) == -1) { + gScore[wid] = tempScore; + fScore[wid] = tempScore + heuristic(w); + openSet.push(wid); // Add node to openSet + cameFrom[wid] = cMinId; + cameFromEdge[wid] = e.id(); + continue; + } + // w already in openSet, but with greater gScore + if (tempScore < gScore[wid]) { + gScore[wid] = tempScore; + fScore[wid] = tempScore + heuristic(w); + cameFrom[wid] = cMinId; + } + } // End of neighbors update + } // End of main loop + // If we've reached here, then we've not reached our goal return { - path: cy.collection(connectedEles, { unique: true }), - found: cy.collection(found) + found: false, + distance: undefined, + path: undefined, + steps: steps }; - }; -}; + } -// search, spanning trees, etc -var elesfn = { - breadthFirstSearch: defineSearch({ bfs: true }), - depthFirstSearch: defineSearch({ dfs: true }) -}; +}; // elesfn -// nice, short mathemathical alias -elesfn.bfs = elesfn.breadthFirstSearch; -elesfn.dfs = elesfn.depthFirstSearch; module.exports = elesfn; /***/ }), -/* 28 */ +/* 35 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -5735,137 +5631,196 @@ var is = __webpack_require__(0); var elesfn = { - closenessCentralityNormalized: function closenessCentralityNormalized(options) { + // Implemented from pseudocode from wikipedia + floydWarshall: function floydWarshall(options) { options = options || {}; var cy = this.cy(); - var harmonic = options.harmonic; - if (harmonic === undefined) { - harmonic = true; + // Weight function - optional + if (options.weight != null && is.fn(options.weight)) { + var weightFn = options.weight; + } else { + // If not specified, assume each edge has equal weight (1) + var weightFn = function weightFn(e) { + return 1; + }; } - var closenesses = {}; - var maxCloseness = 0; + // directed - optional + if (options.directed != null) { + var directed = options.directed; + } else { + var directed = false; + } + + var edges = this.edges().stdFilter(function (e) { + return !e.isLoop(); + }); var nodes = this.nodes(); - var fw = this.floydWarshall({ weight: options.weight, directed: options.directed }); + var numNodes = nodes.length; - // Compute closeness for every node and find the maximum closeness - for (var i = 0; i < nodes.length; i++) { - var currCloseness = 0; - for (var j = 0; j < nodes.length; j++) { - if (i != j) { - var d = fw.distance(nodes[i], nodes[j]); + // mapping: node id -> position in nodes array + var id2position = {}; + for (var i = 0; i < numNodes; i++) { + id2position[nodes[i].id()] = i; + } - if (harmonic) { - currCloseness += 1 / d; - } else { - currCloseness += d; - } + // Initialize distance matrix + var dist = []; + for (var i = 0; i < numNodes; i++) { + var newRow = new Array(numNodes); + for (var j = 0; j < numNodes; j++) { + if (i == j) { + newRow[j] = 0; + } else { + newRow[j] = Infinity; } } + dist.push(newRow); + } - if (!harmonic) { - currCloseness = 1 / currCloseness; + // Initialize matrix used for path reconstruction + // Initialize distance matrix + var next = []; + var edgeNext = []; + + var initMatrix = function initMatrix(next) { + for (var i = 0; i < numNodes; i++) { + var newRow = new Array(numNodes); + for (var j = 0; j < numNodes; j++) { + newRow[j] = undefined; + } + next.push(newRow); } + }; - if (maxCloseness < currCloseness) { - maxCloseness = currCloseness; + initMatrix(next); + initMatrix(edgeNext); + + // Process edges + for (var i = 0; i < edges.length; i++) { + var sourceIndex = id2position[edges[i].source().id()]; + var targetIndex = id2position[edges[i].target().id()]; + var weight = weightFn(edges[i]); + + // Check if already process another edge between same 2 nodes + if (dist[sourceIndex][targetIndex] > weight) { + dist[sourceIndex][targetIndex] = weight; + next[sourceIndex][targetIndex] = targetIndex; + edgeNext[sourceIndex][targetIndex] = edges[i]; } + } - closenesses[nodes[i].id()] = currCloseness; + // If undirected graph, process 'reversed' edges + if (!directed) { + for (var i = 0; i < edges.length; i++) { + var sourceIndex = id2position[edges[i].target().id()]; + var targetIndex = id2position[edges[i].source().id()]; + var weight = weightFn(edges[i]); + + // Check if already process another edge between same 2 nodes + if (dist[sourceIndex][targetIndex] > weight) { + dist[sourceIndex][targetIndex] = weight; + next[sourceIndex][targetIndex] = targetIndex; + edgeNext[sourceIndex][targetIndex] = edges[i]; + } + } } - return { - closeness: function closeness(node) { - if (maxCloseness == 0) { - return 0; + // Main loop + for (var k = 0; k < numNodes; k++) { + for (var i = 0; i < numNodes; i++) { + for (var j = 0; j < numNodes; j++) { + if (dist[i][k] + dist[k][j] < dist[i][j]) { + dist[i][j] = dist[i][k] + dist[k][j]; + next[i][j] = next[i][k]; + } } + } + } - if (is.string(node)) { + // Build result object + var position2id = []; + for (var i = 0; i < numNodes; i++) { + position2id.push(nodes[i].id()); + } + + var res = { + distance: function distance(from, to) { + if (is.string(from)) { // from is a selector string - var node = cy.filter(node)[0].id(); + var fromId = cy.filter(from)[0].id(); } else { // from is a node - var node = node.id(); + var fromId = from.id(); } - return closenesses[node] / maxCloseness; - } - }; - }, - - // Implemented from pseudocode from wikipedia - closenessCentrality: function closenessCentrality(options) { - options = options || {}; + if (is.string(to)) { + // to is a selector string + var toId = cy.filter(to)[0].id(); + } else { + // to is a node + var toId = to.id(); + } - // root - mandatory! - if (options.root != null) { - if (is.string(options.root)) { - // use it as a selector, e.g. "#rootID - var root = this.filter(options.root)[0]; - } else { - var root = options.root[0]; - } - } else { - return undefined; - } + return dist[id2position[fromId]][id2position[toId]]; + }, - // weight - optional - if (options.weight != null && is.fn(options.weight)) { - var weight = options.weight; - } else { - var weight = function weight() { - return 1; - }; - } + path: function path(from, to) { + var reconstructPathAux = function reconstructPathAux(from, to, next, position2id, edgeNext) { + if (from === to) { + return cy.getElementById(position2id[from]); + } + if (next[from][to] === undefined) { + return undefined; + } - // directed - optional - if (options.directed != null && is.bool(options.directed)) { - var directed = options.directed; - } else { - var directed = false; - } + var path = [cy.getElementById(position2id[from])]; + var prev = from; + while (from !== to) { + prev = from; + from = next[from][to]; - var harmonic = options.harmonic; - if (harmonic === undefined) { - harmonic = true; - } + var edge = edgeNext[prev][from]; + path.push(edge); - // we need distance from this node to every other node - var dijkstra = this.dijkstra({ - root: root, - weight: weight, - directed: directed - }); - var totalDistance = 0; + path.push(cy.getElementById(position2id[from])); + } + return path; + }; - var nodes = this.nodes(); - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].id() != root.id()) { - var d = dijkstra.distanceTo(nodes[i]); + if (is.string(from)) { + // from is a selector string + var fromId = cy.filter(from)[0].id(); + } else { + // from is a node + var fromId = from.id(); + } - if (harmonic) { - totalDistance += 1 / d; + if (is.string(to)) { + // to is a selector string + var toId = cy.filter(to)[0].id(); } else { - totalDistance += d; + // to is a node + var toId = to.id(); } + + var pathArr = reconstructPathAux(id2position[fromId], id2position[toId], next, position2id, edgeNext); + + return cy.collection(pathArr); } - } + }; - return harmonic ? totalDistance : 1 / totalDistance; - } // closenessCentrality + return res; + } // floydWarshall }; // elesfn -// nice, short mathemathical alias -elesfn.cc = elesfn.closenessCentrality; -elesfn.ccn = elesfn.closenessCentralityNormalised = elesfn.closenessCentralityNormalized; - module.exports = elesfn; /***/ }), -/* 29 */ +/* 36 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -5876,10 +5831,21 @@ var util = __webpack_require__(1); var elesfn = { - degreeCentralityNormalized: function degreeCentralityNormalized(options) { + // Implemented from pseudocode from wikipedia + bellmanFord: function bellmanFord(options) { + var eles = this; + options = options || {}; - var cy = this.cy(); + // Weight function - optional + if (options.weight != null && is.fn(options.weight)) { + var weightFn = options.weight; + } else { + // If not specified, assume each edge has equal weight (1) + var weightFn = function weightFn(e) { + return 1; + }; + } // directed - optional if (options.directed != null) { @@ -5888,319 +5854,346 @@ var elesfn = { var directed = false; } - var nodes = this.nodes(); - var numNodes = nodes.length; - - if (!directed) { - var degrees = {}; - var maxDegree = 0; - - for (var i = 0; i < numNodes; i++) { - var node = nodes[i]; - // add current node to the current options object and call degreeCentrality - var currDegree = this.degreeCentrality(util.extend({}, options, { root: node })); - if (maxDegree < currDegree.degree) maxDegree = currDegree.degree; - - degrees[node.id()] = currDegree.degree; + // root - mandatory! + if (options.root != null) { + if (is.string(options.root)) { + // use it as a selector, e.g. "#rootID + var source = this.filter(options.root)[0]; + } else { + var source = options.root[0]; } - - return { - degree: function degree(node) { - if (maxDegree == 0) return 0; - - if (is.string(node)) { - // from is a selector string - var node = cy.filter(node)[0].id(); - } else { - // from is a node - var node = node.id(); - } - - return degrees[node] / maxDegree; - } - }; } else { - var indegrees = {}; - var outdegrees = {}; - var maxIndegree = 0; - var maxOutdegree = 0; + return undefined; + } - for (var i = 0; i < numNodes; i++) { - var node = nodes[i]; - // add current node to the current options object and call degreeCentrality - var currDegree = this.degreeCentrality(util.extend({}, options, { root: node })); + var cy = this._private.cy; + var edges = this.edges().stdFilter(function (e) { + return !e.isLoop(); + }); + var nodes = this.nodes(); + var numNodes = nodes.length; - if (maxIndegree < currDegree.indegree) maxIndegree = currDegree.indegree; + // mapping: node id -> position in nodes array + var id2position = {}; + for (var i = 0; i < numNodes; i++) { + id2position[nodes[i].id()] = i; + } - if (maxOutdegree < currDegree.outdegree) maxOutdegree = currDegree.outdegree; + // Initializations + var cost = []; + var predecessor = []; + var predEdge = []; - indegrees[node.id()] = currDegree.indegree; - outdegrees[node.id()] = currDegree.outdegree; + for (var i = 0; i < numNodes; i++) { + if (nodes[i].id() === source.id()) { + cost[i] = 0; + } else { + cost[i] = Infinity; } + predecessor[i] = undefined; + } - return { - indegree: function indegree(node) { - if (maxIndegree == 0) return 0; - - if (is.string(node)) { - // from is a selector string - var node = cy.filter(node)[0].id(); - } else { - // from is a node - var node = node.id(); - } + // Edges relaxation + var flag = false; + for (var i = 1; i < numNodes; i++) { + flag = false; + for (var e = 0; e < edges.length; e++) { + var sourceIndex = id2position[edges[e].source().id()]; + var targetIndex = id2position[edges[e].target().id()]; + var weight = weightFn(edges[e]); - return indegrees[node] / maxIndegree; - }, - outdegree: function outdegree(node) { - if (maxOutdegree == 0) return 0; + var temp = cost[sourceIndex] + weight; + if (temp < cost[targetIndex]) { + cost[targetIndex] = temp; + predecessor[targetIndex] = sourceIndex; + predEdge[targetIndex] = edges[e]; + flag = true; + } - if (is.string(node)) { - // from is a selector string - var node = cy.filter(node)[0].id(); - } else { - // from is a node - var node = node.id(); + // If undirected graph, we need to take into account the 'reverse' edge + if (!directed) { + var temp = cost[targetIndex] + weight; + if (temp < cost[sourceIndex]) { + cost[sourceIndex] = temp; + predecessor[sourceIndex] = targetIndex; + predEdge[sourceIndex] = edges[e]; + flag = true; } - - return outdegrees[node] / maxOutdegree; } + } - }; + if (!flag) { + break; + } } - }, // degreeCentralityNormalized - // Implemented from the algorithm in Opsahl's paper - // "Node centrality in weighted networks: Generalizing degree and shortest paths" - // check the heading 2 "Degree" - degreeCentrality: function degreeCentrality(options) { - options = options || {}; - - var callingEles = this; + if (flag) { + // Check for negative weight cycles + for (var e = 0; e < edges.length; e++) { + var sourceIndex = id2position[edges[e].source().id()]; + var targetIndex = id2position[edges[e].target().id()]; + var weight = weightFn(edges[e]); - // root - mandatory! - if (options != null && options.root != null) { - var root = is.string(options.root) ? this.filter(options.root)[0] : options.root[0]; - } else { - return undefined; + if (cost[sourceIndex] + weight < cost[targetIndex]) { + util.error('Graph contains a negative weight cycle for Bellman-Ford'); + return { pathTo: undefined, + distanceTo: undefined, + hasNegativeWeightCycle: true }; + } + } } - // weight - optional - if (options.weight != null && is.fn(options.weight)) { - var weightFn = options.weight; - } else { - // If not specified, assume each edge has equal weight (1) - var weightFn = function weightFn(e) { - return 1; - }; + // Build result object + var position2id = []; + for (var i = 0; i < numNodes; i++) { + position2id.push(nodes[i].id()); } - // directed - optional - if (options.directed != null) { - var directed = options.directed; - } else { - var directed = false; - } + var res = { + distanceTo: function distanceTo(to) { + if (is.string(to)) { + // to is a selector string + var toId = cy.filter(to)[0].id(); + } else { + // to is a node + var toId = to.id(); + } - // alpha - optional - if (options.alpha != null && is.number(options.alpha)) { - var alpha = options.alpha; - } else { - alpha = 0; - } + return cost[id2position[toId]]; + }, - if (!directed) { - var connEdges = root.connectedEdges().intersection(callingEles); - var k = connEdges.length; - var s = 0; + pathTo: function pathTo(to) { - // Now, sum edge weights - for (var i = 0; i < connEdges.length; i++) { - var edge = connEdges[i]; - s += weightFn(edge); - } + var reconstructPathAux = function reconstructPathAux(predecessor, fromPos, toPos, position2id, acumPath, predEdge) { + for (;;) { + // Add toId to path + acumPath.push(cy.getElementById(position2id[toPos])); + acumPath.push(predEdge[toPos]); - return { - degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha) - }; - } else { - var incoming = root.connectedEdges('edge[target = "' + root.id() + '"]').intersection(callingEles); - var outgoing = root.connectedEdges('edge[source = "' + root.id() + '"]').intersection(callingEles); - var k_in = incoming.length; - var k_out = outgoing.length; - var s_in = 0; - var s_out = 0; + if (fromPos === toPos) { + // reached starting node + return acumPath; + } - // Now, sum incoming edge weights - for (var i = 0; i < incoming.length; i++) { - var edge = incoming[i]; - s_in += weightFn(edge); - } + // If no path exists, discart acumulated path and return undefined + var predPos = predecessor[toPos]; + if (typeof predPos === 'undefined') { + return undefined; + } - // Now, sum outgoing edge weights - for (var i = 0; i < outgoing.length; i++) { - var edge = outgoing[i]; - s_out += weightFn(edge); - } + toPos = predPos; + } + }; - return { - indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha), - outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha) - }; - } - } // degreeCentrality + if (is.string(to)) { + // to is a selector string + var toId = cy.filter(to)[0].id(); + } else { + // to is a node + var toId = to.id(); + } + var path = []; -}; // elesfn + // This returns a reversed path + var res = reconstructPathAux(predecessor, id2position[source.id()], id2position[toId], position2id, path, predEdge); -// nice, short mathemathical alias -elesfn.dc = elesfn.degreeCentrality; -elesfn.dcn = elesfn.degreeCentralityNormalised = elesfn.degreeCentralityNormalized; + // Get it in the correct order and return it + if (res != null) { + res.reverse(); + } -module.exports = elesfn; + return eles.spawn(res); + }, -/***/ }), -/* 30 */ + hasNegativeWeightCycle: false + }; + + return res; + } // bellmanFord + +}; // elesfn + +module.exports = elesfn; + +/***/ }), +/* 37 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var is = __webpack_require__(0); -var Heap = __webpack_require__(8); +var util = __webpack_require__(1); var elesfn = { - dijkstra: function dijkstra(root, weightFn, directed) { - var options; - if (is.plainObject(root) && !is.elementOrCollection(root)) { - options = root; - root = options.root; - weightFn = options.weight; - directed = options.directed; - } + // Computes the minimum cut of an undirected graph + // Returns the correct answer with high probability + kargerStein: function kargerStein(options) { + var eles = this; - var cy = this._private.cy; - weightFn = is.fn(weightFn) ? weightFn : function () { - return 1; - }; // if not specified, assume each edge has equal weight (1) + options = options || {}; - var source = is.string(root) ? this.filter(root)[0] : root[0]; - var dist = {}; - var prev = {}; - var knownDist = {}; + // Function which colapses 2 (meta) nodes into one + // Updates the remaining edge lists + // Receives as a paramater the edge which causes the collapse + var colapse = function colapse(edgeIndex, nodeMap, remainingEdges) { + var edgeInfo = remainingEdges[edgeIndex]; + var sourceIn = edgeInfo[1]; + var targetIn = edgeInfo[2]; + var partition1 = nodeMap[sourceIn]; + var partition2 = nodeMap[targetIn]; - var edges = this.edges().filter(function (ele) { - return !ele.isLoop(); - }); - var nodes = this.nodes(); + // Delete all edges between partition1 and partition2 + var newEdges = remainingEdges.filter(function (edge) { + if (nodeMap[edge[1]] === partition1 && nodeMap[edge[2]] === partition2) { + return false; + } + if (nodeMap[edge[1]] === partition2 && nodeMap[edge[2]] === partition1) { + return false; + } + return true; + }); - var getDist = function getDist(node) { - return dist[node.id()]; - }; + // All edges pointing to partition2 should now point to partition1 + for (var i = 0; i < newEdges.length; i++) { + var edge = newEdges[i]; + if (edge[1] === partition2) { + // Check source + newEdges[i] = edge.slice(0); + newEdges[i][1] = partition1; + } else if (edge[2] === partition2) { + // Check target + newEdges[i] = edge.slice(0); + newEdges[i][2] = partition1; + } + } - var setDist = function setDist(node, d) { - dist[node.id()] = d; + // Move all nodes from partition2 to partition1 + for (var i = 0; i < nodeMap.length; i++) { + if (nodeMap[i] === partition2) { + nodeMap[i] = partition1; + } + } - Q.updateItem(node); + return newEdges; }; - var Q = new Heap(function (a, b) { - return getDist(a) - getDist(b); - }); - - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - - dist[node.id()] = node.same(source) ? 0 : Infinity; - Q.push(node); - } - - var distBetween = function distBetween(u, v) { - var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges); - var smallestDistance = Infinity; - var smallestEdge; + // Contracts a graph until we reach a certain number of meta nodes + var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) { + // Stop condition + if (size <= sizeLimit) { + return remainingEdges; + } - for (var i = 0; i < uvs.length; i++) { - var edge = uvs[i]; - var weight = weightFn(edge); + // Choose an edge randomly + var edgeIndex = Math.floor(Math.random() * remainingEdges.length); - if (weight < smallestDistance || !smallestEdge) { - smallestDistance = weight; - smallestEdge = edge; - } - } + // Colapse graph based on edge + var newEdges = colapse(edgeIndex, metaNodeMap, remainingEdges); - return { - edge: smallestEdge, - dist: smallestDistance - }; + return contractUntil(metaNodeMap, newEdges, size - 1, sizeLimit); }; - while (Q.size() > 0) { - var u = Q.pop(); - var smalletsDist = getDist(u); - var uid = u.id(); - - knownDist[uid] = smalletsDist; + var cy = this._private.cy; + var edges = this.edges().stdFilter(function (e) { + return !e.isLoop(); + }); + var nodes = this.nodes(); + var numNodes = nodes.length; + var numEdges = edges.length; + var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2)); + var stopSize = Math.floor(numNodes / Math.sqrt(2)); - if (smalletsDist === Infinity) { - continue; - } + if (numNodes < 2) { + util.error('At least 2 nodes are required for Karger-Stein algorithm'); + return undefined; + } - var neighbors = u.neighborhood().intersect(nodes); - for (var i = 0; i < neighbors.length; i++) { - var v = neighbors[i]; - var vid = v.id(); - var vDist = distBetween(u, v); + // Create numerical identifiers for each node + // mapping: node id -> position in nodes array + // for reverse mapping, simply use nodes array + var id2position = {}; + for (var i = 0; i < numNodes; i++) { + id2position[nodes[i].id()] = i; + } - var alt = smalletsDist + vDist.dist; + // Now store edge destination as indexes + // Format for each edge (edge index, source node index, target node index) + var edgeIndexes = []; + for (var i = 0; i < numEdges; i++) { + var e = edges[i]; + edgeIndexes.push([i, id2position[e.source().id()], id2position[e.target().id()]]); + } - if (alt < getDist(v)) { - setDist(v, alt); + // We will store the best cut found here + var minCutSize = Infinity; + var minCut; - prev[vid] = { - node: u, - edge: vDist.edge - }; - } - } // for - } // while + // Initial meta node partition + var originalMetaNode = []; + for (var i = 0; i < numNodes; i++) { + originalMetaNode.push(i); + } - return { - distanceTo: function distanceTo(node) { - var target = is.string(node) ? nodes.filter(node)[0] : node[0]; + // Main loop + for (var iter = 0; iter <= numIter; iter++) { + // Create new meta node partition + var metaNodeMap = originalMetaNode.slice(0); - return knownDist[target.id()]; - }, + // Contract until stop point (stopSize nodes) + var edgesState = contractUntil(metaNodeMap, edgeIndexes, numNodes, stopSize); - pathTo: function pathTo(node) { - var target = is.string(node) ? nodes.filter(node)[0] : node[0]; - var S = []; - var u = target; + // Create a copy of the colapsed nodes state + var metaNodeMap2 = metaNodeMap.slice(0); - if (target.length > 0) { - S.unshift(target); + // Run 2 iterations starting in the stop state + var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2); + var res2 = contractUntil(metaNodeMap2, edgesState, stopSize, 2); - while (prev[u.id()]) { - var p = prev[u.id()]; + // Is any of the 2 results the best cut so far? + if (res1.length <= res2.length && res1.length < minCutSize) { + minCutSize = res1.length; + minCut = [res1, metaNodeMap]; + } else if (res2.length <= res1.length && res2.length < minCutSize) { + minCutSize = res2.length; + minCut = [res2, metaNodeMap2]; + } + } // end of main loop - S.unshift(p.edge); - S.unshift(p.node); - u = p.node; - } - } + // Construct result + var resEdges = minCut[0].map(function (e) { + return edges[e[0]]; + }); + var partition1 = []; + var partition2 = []; - return cy.collection(S); + // traverse metaNodeMap for best cut + var witnessNodePartition = minCut[1][0]; + for (var i = 0; i < minCut[1].length; i++) { + var partitionId = minCut[1][i]; + if (partitionId === witnessNodePartition) { + partition1.push(nodes[i]); + } else { + partition2.push(nodes[i]); } + } + + var ret = { + cut: eles.spawn(cy, resEdges), + partition1: eles.spawn(partition1), + partition2: eles.spawn(partition2) }; + + return ret; } -}; +}; // elesfn + module.exports = elesfn; /***/ }), -/* 31 */ +/* 38 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -6210,15 +6203,48 @@ var is = __webpack_require__(0); var elesfn = { - // Implemented from pseudocode from wikipedia - floydWarshall: function floydWarshall(options) { + pageRank: function pageRank(options) { options = options || {}; - var cy = this.cy(); + var normalizeVector = function normalizeVector(vector) { + var length = vector.length; - // Weight function - optional - if (options.weight != null && is.fn(options.weight)) { - var weightFn = options.weight; + // First, get sum of all elements + var total = 0; + for (var i = 0; i < length; i++) { + total += vector[i]; + } + + // Now, divide each by the sum of all elements + for (var i = 0; i < length; i++) { + vector[i] = vector[i] / total; + } + }; + + // dampingFactor - optional + if (options != null && options.dampingFactor != null) { + var dampingFactor = options.dampingFactor; + } else { + var dampingFactor = 0.8; // Default damping factor + } + + // desired precision - optional + if (options != null && options.precision != null) { + var epsilon = options.precision; + } else { + var epsilon = 0.000001; // Default precision + } + + // Max number of iterations - optional + if (options != null && options.iterations != null) { + var numIter = options.iterations; + } else { + var numIter = 200; // Default number of iterations + } + + // Weight function - optional + if (options != null && options.weight != null && is.fn(options.weight)) { + var weightFn = options.weight; } else { // If not specified, assume each edge has equal weight (1) var weightFn = function weightFn(e) { @@ -6226,374 +6252,327 @@ var elesfn = { }; } - // directed - optional - if (options.directed != null) { - var directed = options.directed; - } else { - var directed = false; - } - + var cy = this._private.cy; var edges = this.edges().stdFilter(function (e) { return !e.isLoop(); }); var nodes = this.nodes(); var numNodes = nodes.length; + var numEdges = edges.length; + // Create numerical identifiers for each node // mapping: node id -> position in nodes array + // for reverse mapping, simply use nodes array var id2position = {}; for (var i = 0; i < numNodes; i++) { id2position[nodes[i].id()] = i; } - // Initialize distance matrix - var dist = []; + // Construct transposed adjacency matrix + // First lets have a zeroed matrix of the right size + // We'll also keep track of the sum of each column + var matrix = []; + var columnSum = []; + var additionalProb = (1 - dampingFactor) / numNodes; + + // Create null matric for (var i = 0; i < numNodes; i++) { - var newRow = new Array(numNodes); + var newRow = []; for (var j = 0; j < numNodes; j++) { - if (i == j) { - newRow[j] = 0; - } else { - newRow[j] = Infinity; - } + newRow.push(0.0); } - dist.push(newRow); + matrix.push(newRow); + columnSum.push(0.0); } - // Initialize matrix used for path reconstruction - // Initialize distance matrix - var next = []; - var edgeNext = []; - - var initMatrix = function initMatrix(next) { - for (var i = 0; i < numNodes; i++) { - var newRow = new Array(numNodes); - for (var j = 0; j < numNodes; j++) { - newRow[j] = undefined; - } - next.push(newRow); - } - }; - - initMatrix(next); - initMatrix(edgeNext); + // Now, process edges + for (var i = 0; i < numEdges; i++) { + var edge = edges[i]; + var s = id2position[edge.source().id()]; + var t = id2position[edge.target().id()]; + var w = weightFn(edge); - // Process edges - for (var i = 0; i < edges.length; i++) { - var sourceIndex = id2position[edges[i].source().id()]; - var targetIndex = id2position[edges[i].target().id()]; - var weight = weightFn(edges[i]); + // Update matrix + matrix[t][s] += w; - // Check if already process another edge between same 2 nodes - if (dist[sourceIndex][targetIndex] > weight) { - dist[sourceIndex][targetIndex] = weight; - next[sourceIndex][targetIndex] = targetIndex; - edgeNext[sourceIndex][targetIndex] = edges[i]; - } + // Update column sum + columnSum[s] += w; } - // If undirected graph, process 'reversed' edges - if (!directed) { - for (var i = 0; i < edges.length; i++) { - var sourceIndex = id2position[edges[i].target().id()]; - var targetIndex = id2position[edges[i].source().id()]; - var weight = weightFn(edges[i]); - - // Check if already process another edge between same 2 nodes - if (dist[sourceIndex][targetIndex] > weight) { - dist[sourceIndex][targetIndex] = weight; - next[sourceIndex][targetIndex] = targetIndex; - edgeNext[sourceIndex][targetIndex] = edges[i]; + // Add additional probability based on damping factor + // Also, take into account columns that have sum = 0 + var p = 1.0 / numNodes + additionalProb; // Shorthand + // Traverse matrix, column by column + for (var j = 0; j < numNodes; j++) { + if (columnSum[j] === 0) { + // No 'links' out from node jth, assume equal probability for each possible node + for (var i = 0; i < numNodes; i++) { + matrix[i][j] = p; } - } - } - - // Main loop - for (var k = 0; k < numNodes; k++) { - for (var i = 0; i < numNodes; i++) { - for (var j = 0; j < numNodes; j++) { - if (dist[i][k] + dist[k][j] < dist[i][j]) { - dist[i][j] = dist[i][k] + dist[k][j]; - next[i][j] = next[i][k]; - } + } else { + // Node jth has outgoing link, compute normalized probabilities + for (var i = 0; i < numNodes; i++) { + matrix[i][j] = matrix[i][j] / columnSum[j] + additionalProb; } } } - // Build result object - var position2id = []; + // Compute dominant eigenvector using power method + var eigenvector = []; + var nullVector = []; + var previous; + + // Start with a vector of all 1's + // Also, initialize a null vector which will be used as shorthand for (var i = 0; i < numNodes; i++) { - position2id.push(nodes[i].id()); + eigenvector.push(1.0); + nullVector.push(0.0); } - var res = { - distance: function distance(from, to) { - if (is.string(from)) { - // from is a selector string - var fromId = cy.filter(from)[0].id(); - } else { - // from is a node - var fromId = from.id(); - } + for (var iter = 0; iter < numIter; iter++) { + // New array with all 0's + var temp = nullVector.slice(0); - if (is.string(to)) { - // to is a selector string - var toId = cy.filter(to)[0].id(); - } else { - // to is a node - var toId = to.id(); + // Multiply matrix with previous result + for (var i = 0; i < numNodes; i++) { + for (var j = 0; j < numNodes; j++) { + temp[i] += matrix[i][j] * eigenvector[j]; } + } - return dist[id2position[fromId]][id2position[toId]]; - }, - - path: function path(from, to) { - var reconstructPathAux = function reconstructPathAux(from, to, next, position2id, edgeNext) { - if (from === to) { - return cy.getElementById(position2id[from]); - } - if (next[from][to] === undefined) { - return undefined; - } - - var path = [cy.getElementById(position2id[from])]; - var prev = from; - while (from !== to) { - prev = from; - from = next[from][to]; - - var edge = edgeNext[prev][from]; - path.push(edge); + normalizeVector(temp); + previous = eigenvector; + eigenvector = temp; - path.push(cy.getElementById(position2id[from])); - } - return path; - }; + var diff = 0; + // Compute difference (squared module) of both vectors + for (var i = 0; i < numNodes; i++) { + diff += Math.pow(previous[i] - eigenvector[i], 2); + } - if (is.string(from)) { - // from is a selector string - var fromId = cy.filter(from)[0].id(); - } else { - // from is a node - var fromId = from.id(); - } + // If difference is less than the desired threshold, stop iterating + if (diff < epsilon) { + break; + } + } - if (is.string(to)) { - // to is a selector string - var toId = cy.filter(to)[0].id(); + // Construct result + var res = { + rank: function rank(node) { + if (is.string(node)) { + // is a selector string + var nodeId = cy.filter(node)[0].id(); } else { - // to is a node - var toId = to.id(); + // is a node object + var nodeId = node.id(); } - - var pathArr = reconstructPathAux(id2position[fromId], id2position[toId], next, position2id, edgeNext); - - return cy.collection(pathArr); + return eigenvector[id2position[nodeId]]; } }; return res; - } // floydWarshall + } // pageRank }; // elesfn module.exports = elesfn; /***/ }), -/* 32 */ +/* 39 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +var is = __webpack_require__(0); var util = __webpack_require__(1); -var elesfn = {}; - -[__webpack_require__(27), __webpack_require__(30), __webpack_require__(34), __webpack_require__(24), __webpack_require__(31), __webpack_require__(25), __webpack_require__(33), __webpack_require__(35), __webpack_require__(29), __webpack_require__(28), __webpack_require__(26)].forEach(function (props) { - util.extend(elesfn, props); -}); +var elesfn = { -module.exports = elesfn; + degreeCentralityNormalized: function degreeCentralityNormalized(options) { + options = options || {}; -/***/ }), -/* 33 */ -/***/ (function(module, exports, __webpack_require__) { + var cy = this.cy(); -"use strict"; + // directed - optional + if (options.directed != null) { + var directed = options.directed; + } else { + var directed = false; + } + var nodes = this.nodes(); + var numNodes = nodes.length; -var util = __webpack_require__(1); + if (!directed) { + var degrees = {}; + var maxDegree = 0; -var elesfn = { + for (var i = 0; i < numNodes; i++) { + var node = nodes[i]; + // add current node to the current options object and call degreeCentrality + var currDegree = this.degreeCentrality(util.extend({}, options, { root: node })); + if (maxDegree < currDegree.degree) maxDegree = currDegree.degree; - // Computes the minimum cut of an undirected graph - // Returns the correct answer with high probability - kargerStein: function kargerStein(options) { - var eles = this; + degrees[node.id()] = currDegree.degree; + } - options = options || {}; + return { + degree: function degree(node) { + if (maxDegree == 0) return 0; - // Function which colapses 2 (meta) nodes into one - // Updates the remaining edge lists - // Receives as a paramater the edge which causes the collapse - var colapse = function colapse(edgeIndex, nodeMap, remainingEdges) { - var edgeInfo = remainingEdges[edgeIndex]; - var sourceIn = edgeInfo[1]; - var targetIn = edgeInfo[2]; - var partition1 = nodeMap[sourceIn]; - var partition2 = nodeMap[targetIn]; + if (is.string(node)) { + // from is a selector string + var node = cy.filter(node)[0].id(); + } else { + // from is a node + var node = node.id(); + } - // Delete all edges between partition1 and partition2 - var newEdges = remainingEdges.filter(function (edge) { - if (nodeMap[edge[1]] === partition1 && nodeMap[edge[2]] === partition2) { - return false; - } - if (nodeMap[edge[1]] === partition2 && nodeMap[edge[2]] === partition1) { - return false; + return degrees[node] / maxDegree; } - return true; - }); + }; + } else { + var indegrees = {}; + var outdegrees = {}; + var maxIndegree = 0; + var maxOutdegree = 0; - // All edges pointing to partition2 should now point to partition1 - for (var i = 0; i < newEdges.length; i++) { - var edge = newEdges[i]; - if (edge[1] === partition2) { - // Check source - newEdges[i] = edge.slice(0); - newEdges[i][1] = partition1; - } else if (edge[2] === partition2) { - // Check target - newEdges[i] = edge.slice(0); - newEdges[i][2] = partition1; - } - } + for (var i = 0; i < numNodes; i++) { + var node = nodes[i]; + // add current node to the current options object and call degreeCentrality + var currDegree = this.degreeCentrality(util.extend({}, options, { root: node })); - // Move all nodes from partition2 to partition1 - for (var i = 0; i < nodeMap.length; i++) { - if (nodeMap[i] === partition2) { - nodeMap[i] = partition1; - } - } + if (maxIndegree < currDegree.indegree) maxIndegree = currDegree.indegree; - return newEdges; - }; + if (maxOutdegree < currDegree.outdegree) maxOutdegree = currDegree.outdegree; - // Contracts a graph until we reach a certain number of meta nodes - var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) { - // Stop condition - if (size <= sizeLimit) { - return remainingEdges; + indegrees[node.id()] = currDegree.indegree; + outdegrees[node.id()] = currDegree.outdegree; } - // Choose an edge randomly - var edgeIndex = Math.floor(Math.random() * remainingEdges.length); + return { + indegree: function indegree(node) { + if (maxIndegree == 0) return 0; - // Colapse graph based on edge - var newEdges = colapse(edgeIndex, metaNodeMap, remainingEdges); + if (is.string(node)) { + // from is a selector string + var node = cy.filter(node)[0].id(); + } else { + // from is a node + var node = node.id(); + } - return contractUntil(metaNodeMap, newEdges, size - 1, sizeLimit); - }; + return indegrees[node] / maxIndegree; + }, + outdegree: function outdegree(node) { + if (maxOutdegree == 0) return 0; - var cy = this._private.cy; - var edges = this.edges().stdFilter(function (e) { - return !e.isLoop(); - }); - var nodes = this.nodes(); - var numNodes = nodes.length; - var numEdges = edges.length; - var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2)); - var stopSize = Math.floor(numNodes / Math.sqrt(2)); + if (is.string(node)) { + // from is a selector string + var node = cy.filter(node)[0].id(); + } else { + // from is a node + var node = node.id(); + } - if (numNodes < 2) { - util.error('At least 2 nodes are required for Karger-Stein algorithm'); - return undefined; - } + return outdegrees[node] / maxOutdegree; + } - // Create numerical identifiers for each node - // mapping: node id -> position in nodes array - // for reverse mapping, simply use nodes array - var id2position = {}; - for (var i = 0; i < numNodes; i++) { - id2position[nodes[i].id()] = i; + }; } + }, // degreeCentralityNormalized - // Now store edge destination as indexes - // Format for each edge (edge index, source node index, target node index) - var edgeIndexes = []; - for (var i = 0; i < numEdges; i++) { - var e = edges[i]; - edgeIndexes.push([i, id2position[e.source().id()], id2position[e.target().id()]]); - } + // Implemented from the algorithm in Opsahl's paper + // "Node centrality in weighted networks: Generalizing degree and shortest paths" + // check the heading 2 "Degree" + degreeCentrality: function degreeCentrality(options) { + options = options || {}; - // We will store the best cut found here - var minCutSize = Infinity; - var minCut; + var callingEles = this; - // Initial meta node partition - var originalMetaNode = []; - for (var i = 0; i < numNodes; i++) { - originalMetaNode.push(i); + // root - mandatory! + if (options != null && options.root != null) { + var root = is.string(options.root) ? this.filter(options.root)[0] : options.root[0]; + } else { + return undefined; } - // Main loop - for (var iter = 0; iter <= numIter; iter++) { - // Create new meta node partition - var metaNodeMap = originalMetaNode.slice(0); + // weight - optional + if (options.weight != null && is.fn(options.weight)) { + var weightFn = options.weight; + } else { + // If not specified, assume each edge has equal weight (1) + var weightFn = function weightFn(e) { + return 1; + }; + } - // Contract until stop point (stopSize nodes) - var edgesState = contractUntil(metaNodeMap, edgeIndexes, numNodes, stopSize); + // directed - optional + if (options.directed != null) { + var directed = options.directed; + } else { + var directed = false; + } - // Create a copy of the colapsed nodes state - var metaNodeMap2 = metaNodeMap.slice(0); + // alpha - optional + if (options.alpha != null && is.number(options.alpha)) { + var alpha = options.alpha; + } else { + alpha = 0; + } - // Run 2 iterations starting in the stop state - var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2); - var res2 = contractUntil(metaNodeMap2, edgesState, stopSize, 2); + if (!directed) { + var connEdges = root.connectedEdges().intersection(callingEles); + var k = connEdges.length; + var s = 0; - // Is any of the 2 results the best cut so far? - if (res1.length <= res2.length && res1.length < minCutSize) { - minCutSize = res1.length; - minCut = [res1, metaNodeMap]; - } else if (res2.length <= res1.length && res2.length < minCutSize) { - minCutSize = res2.length; - minCut = [res2, metaNodeMap2]; + // Now, sum edge weights + for (var i = 0; i < connEdges.length; i++) { + var edge = connEdges[i]; + s += weightFn(edge); } - } // end of main loop + return { + degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha) + }; + } else { + var incoming = root.connectedEdges('edge[target = "' + root.id() + '"]').intersection(callingEles); + var outgoing = root.connectedEdges('edge[source = "' + root.id() + '"]').intersection(callingEles); + var k_in = incoming.length; + var k_out = outgoing.length; + var s_in = 0; + var s_out = 0; - // Construct result - var resEdges = minCut[0].map(function (e) { - return edges[e[0]]; - }); - var partition1 = []; - var partition2 = []; + // Now, sum incoming edge weights + for (var i = 0; i < incoming.length; i++) { + var edge = incoming[i]; + s_in += weightFn(edge); + } - // traverse metaNodeMap for best cut - var witnessNodePartition = minCut[1][0]; - for (var i = 0; i < minCut[1].length; i++) { - var partitionId = minCut[1][i]; - if (partitionId === witnessNodePartition) { - partition1.push(nodes[i]); - } else { - partition2.push(nodes[i]); + // Now, sum outgoing edge weights + for (var i = 0; i < outgoing.length; i++) { + var edge = outgoing[i]; + s_out += weightFn(edge); } - } - var ret = { - cut: eles.spawn(cy, resEdges), - partition1: eles.spawn(partition1), - partition2: eles.spawn(partition2) - }; + return { + indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha), + outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha) + }; + } + } // degreeCentrality - return ret; - } }; // elesfn +// nice, short mathemathical alias +elesfn.dc = elesfn.degreeCentrality; +elesfn.dcn = elesfn.degreeCentralityNormalised = elesfn.degreeCentralityNormalized; module.exports = elesfn; /***/ }), -/* 34 */ +/* 40 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -6601,262 +6580,348 @@ module.exports = elesfn; var is = __webpack_require__(0); -// search, spanning trees, etc var elesfn = { - // kruskal's algorithm (finds min spanning tree, assuming undirected graph) - // implemented from pseudocode from wikipedia - kruskal: function kruskal(weightFn) { + closenessCentralityNormalized: function closenessCentralityNormalized(options) { + options = options || {}; + var cy = this.cy(); - weightFn = is.fn(weightFn) ? weightFn : function () { - return 1; - }; // if not specified, assume each edge has equal weight (1) + var harmonic = options.harmonic; + if (harmonic === undefined) { + harmonic = true; + } - function findSet(ele) { - for (var i = 0; i < forest.length; i++) { - var eles = forest[i]; + var closenesses = {}; + var maxCloseness = 0; + var nodes = this.nodes(); + var fw = this.floydWarshall({ weight: options.weight, directed: options.directed }); - if (eles.anySame(ele)) { - return { - eles: eles, - index: i - }; + // Compute closeness for every node and find the maximum closeness + for (var i = 0; i < nodes.length; i++) { + var currCloseness = 0; + for (var j = 0; j < nodes.length; j++) { + if (i != j) { + var d = fw.distance(nodes[i], nodes[j]); + + if (harmonic) { + currCloseness += 1 / d; + } else { + currCloseness += d; + } } } + + if (!harmonic) { + currCloseness = 1 / currCloseness; + } + + if (maxCloseness < currCloseness) { + maxCloseness = currCloseness; + } + + closenesses[nodes[i].id()] = currCloseness; } - var A = cy.collection(cy, []); - var forest = []; - var nodes = this.nodes(); + return { + closeness: function closeness(node) { + if (maxCloseness == 0) { + return 0; + } - for (var i = 0; i < nodes.length; i++) { - forest.push(nodes[i].collection()); + if (is.string(node)) { + // from is a selector string + var node = cy.filter(node)[0].id(); + } else { + // from is a node + var node = node.id(); + } + + return closenesses[node] / maxCloseness; + } + }; + }, + + // Implemented from pseudocode from wikipedia + closenessCentrality: function closenessCentrality(options) { + options = options || {}; + + // root - mandatory! + if (options.root != null) { + if (is.string(options.root)) { + // use it as a selector, e.g. "#rootID + var root = this.filter(options.root)[0]; + } else { + var root = options.root[0]; + } + } else { + return undefined; } - var edges = this.edges(); - var S = edges.toArray().sort(function (a, b) { - var weightA = weightFn(a); - var weightB = weightFn(b); + // weight - optional + if (options.weight != null && is.fn(options.weight)) { + var weight = options.weight; + } else { + var weight = function weight() { + return 1; + }; + } - return weightA - weightB; - }); + // directed - optional + if (options.directed != null && is.bool(options.directed)) { + var directed = options.directed; + } else { + var directed = false; + } - for (var i = 0; i < S.length; i++) { - var edge = S[i]; - var u = edge.source()[0]; - var v = edge.target()[0]; - var setU = findSet(u); - var setV = findSet(v); + var harmonic = options.harmonic; + if (harmonic === undefined) { + harmonic = true; + } - if (setU.index !== setV.index) { - A = A.add(edge); + // we need distance from this node to every other node + var dijkstra = this.dijkstra({ + root: root, + weight: weight, + directed: directed + }); + var totalDistance = 0; - // combine forests for u and v - forest[setU.index] = setU.eles.add(setV.eles); - forest.splice(setV.index, 1); + var nodes = this.nodes(); + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].id() != root.id()) { + var d = dijkstra.distanceTo(nodes[i]); + + if (harmonic) { + totalDistance += 1 / d; + } else { + totalDistance += d; + } } } - return nodes.add(A); - } -}; + return harmonic ? totalDistance : 1 / totalDistance; + } // closenessCentrality + +}; // elesfn + +// nice, short mathemathical alias +elesfn.cc = elesfn.closenessCentrality; +elesfn.ccn = elesfn.closenessCentralityNormalised = elesfn.closenessCentralityNormalized; module.exports = elesfn; /***/ }), -/* 35 */ +/* 41 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var is = __webpack_require__(0); +var Heap = __webpack_require__(9); var elesfn = { - pageRank: function pageRank(options) { + // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes + betweennessCentrality: function betweennessCentrality(options) { options = options || {}; - var normalizeVector = function normalizeVector(vector) { - var length = vector.length; + // Weight - optional + var weighted, weightFn; + if (is.fn(options.weight)) { + weightFn = options.weight; + weighted = true; + } else { + weighted = false; + } - // First, get sum of all elements - var total = 0; - for (var i = 0; i < length; i++) { - total += vector[i]; - } + // Directed - default false + var directed = options.directed != null ? options.directed : false; - // Now, divide each by the sum of all elements - for (var i = 0; i < length; i++) { - vector[i] = vector[i] / total; - } - }; + var cy = this._private.cy; - // dampingFactor - optional - if (options != null && options.dampingFactor != null) { - var dampingFactor = options.dampingFactor; - } else { - var dampingFactor = 0.8; // Default damping factor - } + // starting + var V = this.nodes(); + var A = {}; + var _C = {}; + var max = 0; + var C = { + set: function set(key, val) { + _C[key] = val; - // desired precision - optional - if (options != null && options.precision != null) { - var epsilon = options.precision; - } else { - var epsilon = 0.000001; // Default precision - } + if (val > max) { + max = val; + } + }, - // Max number of iterations - optional - if (options != null && options.iterations != null) { - var numIter = options.iterations; - } else { - var numIter = 200; // Default number of iterations - } + get: function get(key) { + return _C[key]; + } + }; - // Weight function - optional - if (options != null && options.weight != null && is.fn(options.weight)) { - var weightFn = options.weight; - } else { - // If not specified, assume each edge has equal weight (1) - var weightFn = function weightFn(e) { - return 1; - }; - } + // A contains the neighborhoods of every node + for (var i = 0; i < V.length; i++) { + var v = V[i]; + var vid = v.id(); - var cy = this._private.cy; - var edges = this.edges().stdFilter(function (e) { - return !e.isLoop(); - }); - var nodes = this.nodes(); - var numNodes = nodes.length; - var numEdges = edges.length; + if (directed) { + A[vid] = v.outgoers().nodes(); // get outgoers of every node + } else { + A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node + } - // Create numerical identifiers for each node - // mapping: node id -> position in nodes array - // for reverse mapping, simply use nodes array - var id2position = {}; - for (var i = 0; i < numNodes; i++) { - id2position[nodes[i].id()] = i; + C.set(vid, 0); } - // Construct transposed adjacency matrix - // First lets have a zeroed matrix of the right size - // We'll also keep track of the sum of each column - var matrix = []; - var columnSum = []; - var additionalProb = (1 - dampingFactor) / numNodes; + for (var s = 0; s < V.length; s++) { + var sid = V[s].id(); + var S = []; // stack + var P = {}; + var g = {}; + var d = {}; + var Q = new Heap(function (a, b) { + return d[a] - d[b]; + }); // queue - // Create null matric - for (var i = 0; i < numNodes; i++) { - var newRow = []; - for (var j = 0; j < numNodes; j++) { - newRow.push(0.0); + // init dictionaries + for (var i = 0; i < V.length; i++) { + var vid = V[i].id(); + + P[vid] = []; + g[vid] = 0; + d[vid] = Infinity; } - matrix.push(newRow); - columnSum.push(0.0); - } - // Now, process edges - for (var i = 0; i < numEdges; i++) { - var edge = edges[i]; - var s = id2position[edge.source().id()]; - var t = id2position[edge.target().id()]; - var w = weightFn(edge); + g[sid] = 1; // sigma + d[sid] = 0; // distance to s - // Update matrix - matrix[t][s] += w; + Q.push(sid); - // Update column sum - columnSum[s] += w; - } + while (!Q.empty()) { + var v = Q.pop(); - // Add additional probability based on damping factor - // Also, take into account columns that have sum = 0 - var p = 1.0 / numNodes + additionalProb; // Shorthand - // Traverse matrix, column by column - for (var j = 0; j < numNodes; j++) { - if (columnSum[j] === 0) { - // No 'links' out from node jth, assume equal probability for each possible node - for (var i = 0; i < numNodes; i++) { - matrix[i][j] = p; - } - } else { - // Node jth has outgoing link, compute normalized probabilities - for (var i = 0; i < numNodes; i++) { - matrix[i][j] = matrix[i][j] / columnSum[j] + additionalProb; - } - } - } + S.push(v); - // Compute dominant eigenvector using power method - var eigenvector = []; - var nullVector = []; - var previous; + if (weighted) { + for (var j = 0; j < A[v].length; j++) { + var w = A[v][j]; + var vEle = cy.getElementById(v); - // Start with a vector of all 1's - // Also, initialize a null vector which will be used as shorthand - for (var i = 0; i < numNodes; i++) { - eigenvector.push(1.0); - nullVector.push(0.0); - } + var edge; + if (vEle.edgesTo(w).length > 0) { + edge = vEle.edgesTo(w)[0]; + } else { + edge = w.edgesTo(vEle)[0]; + } - for (var iter = 0; iter < numIter; iter++) { - // New array with all 0's - var temp = nullVector.slice(0); + var edgeWeight = weightFn(edge); - // Multiply matrix with previous result - for (var i = 0; i < numNodes; i++) { - for (var j = 0; j < numNodes; j++) { - temp[i] += matrix[i][j] * eigenvector[j]; + w = w.id(); + + if (d[w] > d[v] + edgeWeight) { + d[w] = d[v] + edgeWeight; + + if (Q.nodes.indexOf(w) < 0) { + //if w is not in Q + Q.push(w); + } else { + // update position if w is in Q + Q.updateItem(w); + } + + g[w] = 0; + P[w] = []; + } + + if (d[w] == d[v] + edgeWeight) { + g[w] = g[w] + g[v]; + P[w].push(v); + } + } + } else { + for (var j = 0; j < A[v].length; j++) { + var w = A[v][j].id(); + + if (d[w] == Infinity) { + Q.push(w); + + d[w] = d[v] + 1; + } + + if (d[w] == d[v] + 1) { + g[w] = g[w] + g[v]; + P[w].push(v); + } + } } } - normalizeVector(temp); - previous = eigenvector; - eigenvector = temp; - - var diff = 0; - // Compute difference (squared module) of both vectors - for (var i = 0; i < numNodes; i++) { - diff += Math.pow(previous[i] - eigenvector[i], 2); + var e = {}; + for (var i = 0; i < V.length; i++) { + e[V[i].id()] = 0; } - // If difference is less than the desired threshold, stop iterating - if (diff < epsilon) { - break; + while (S.length > 0) { + var w = S.pop(); + + for (var j = 0; j < P[w].length; j++) { + var v = P[w][j]; + + e[v] = e[v] + g[v] / g[w] * (1 + e[w]); + + if (w != V[s].id()) { + C.set(w, C.get(w) + e[w]); + } + } } } - // Construct result - var res = { - rank: function rank(node) { + var ret = { + betweenness: function betweenness(node) { if (is.string(node)) { - // is a selector string - var nodeId = cy.filter(node)[0].id(); + var node = cy.filter(node).id(); } else { - // is a node object - var nodeId = node.id(); + var node = node.id(); } - return eigenvector[id2position[nodeId]]; + + return C.get(node); + }, + + betweennessNormalized: function betweennessNormalized(node) { + if (max == 0) return 0; + + if (is.string(node)) { + var node = cy.filter(node).id(); + } else { + var node = node.id(); + } + + return C.get(node) / max; } }; - return res; - } // pageRank + // alias + ret.betweennessNormalised = ret.betweennessNormalized; + + return ret; + } // betweennessCentrality }; // elesfn +// nice, short mathemathical alias +elesfn.bc = elesfn.betweennessCentrality; + module.exports = elesfn; /***/ }), -/* 36 */ +/* 42 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(3); +var define = __webpack_require__(4); var elesfn = { animate: define.animate(), @@ -6871,4189 +6936,4397 @@ var elesfn = { module.exports = elesfn; /***/ }), -/* 37 */ +/* 43 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Set = __webpack_require__(9); - -var elesfn = { - classes: function classes(_classes) { - _classes = (_classes || '').match(/\S+/g) || []; - var self = this; - var changed = []; - var classesMap = new Set(_classes); - - // check and update each ele - - var _loop = function _loop(j) { - var ele = self[j]; - var _p = ele._private; - var eleClasses = _p.classes; - var changedEle = false; - - // check if ele has all of the passed classes - classesMap.forEach(function (cls) { - var eleHasClass = eleClasses.has(cls); +var util = __webpack_require__(1); +var Animation = __webpack_require__(44); +var math = __webpack_require__(2); +var is = __webpack_require__(0); - if (!eleHasClass) { - changedEle = true; - } - }); +var define = { - // check if ele has classes outside of those passed - if (!changedEle) { - eleClasses.forEach(function (eleCls) { - var specdClass = classesMap.has(eleCls); + animated: function animated() { + return function animatedImpl() { + var self = this; + var selfIsArrayLike = self.length !== undefined; + var all = selfIsArrayLike ? self : [self]; // put in array if not array-like + var cy = this._private.cy || this; - if (!specdClass) { - changedEle = true; - } - }); + if (!cy.styleEnabled()) { + return false; } - if (changedEle) { - _p.classes = new Set(classesMap); + var ele = all[0]; - changed.push(ele); + if (ele) { + return ele._private.animation.current.length > 0; } }; + }, // animated - for (var j = 0; j < self.length; j++) { - _loop(j); - } + clearQueue: function clearQueue() { + return function clearQueueImpl() { + var self = this; + var selfIsArrayLike = self.length !== undefined; + var all = selfIsArrayLike ? self : [self]; // put in array if not array-like + var cy = this._private.cy || this; - // trigger update style on those eles that had class changes - if (changed.length > 0) { - this.spawn(changed).updateStyle().emit('class'); - } - - return self; - }, + if (!cy.styleEnabled()) { + return this; + } - addClass: function addClass(classes) { - return this.toggleClass(classes, true); - }, + for (var i = 0; i < all.length; i++) { + var ele = all[i]; + ele._private.animation.queue = []; + } - hasClass: function hasClass(className) { - var ele = this[0]; - return ele != null && ele._private.classes.has(className); - }, + return this; + }; + }, // clearQueue - toggleClass: function toggleClass(classesStr, toggle) { - var classes = classesStr.match(/\S+/g) || []; - var self = this; - var changed = []; // eles who had classes changed + delay: function delay() { + return function delayImpl(time, complete) { + var cy = this._private.cy || this; - for (var i = 0, il = self.length; i < il; i++) { - var _ele = self[i]; - var _changedEle = false; + if (!cy.styleEnabled()) { + return this; + } - for (var j = 0; j < classes.length; j++) { - var cls = classes[j]; - var _eleClasses = _ele._private.classes; - var hasClass = _eleClasses.has(cls); - var shouldAdd = toggle || toggle === undefined && !hasClass; + return this.animate({ + delay: time, + duration: time, + complete: complete + }); + }; + }, // delay - if (shouldAdd) { - _eleClasses.add(cls); + delayAnimation: function delayAnimation() { + return function delayAnimationImpl(time, complete) { + var cy = this._private.cy || this; - if (!hasClass && !_changedEle) { - changed.push(_ele); - _changedEle = true; - } - } else { - // then remove - _eleClasses.delete(cls); + if (!cy.styleEnabled()) { + return this; + } - if (hasClass && !_changedEle) { - changed.push(_ele); - _changedEle = true; - } - } - } // for j classes - } // for i eles + return this.animation({ + delay: time, + duration: time, + complete: complete + }); + }; + }, // delay - // trigger update style on those eles that had class changes - if (changed.length > 0) { - this.spawn(changed).updateStyle().emit('class'); - } + animation: function animation() { + return function animationImpl(properties, params) { + var self = this; + var selfIsArrayLike = self.length !== undefined; + var all = selfIsArrayLike ? self : [self]; // put in array if not array-like + var cy = this._private.cy || this; + var isCore = !selfIsArrayLike; + var isEles = !isCore; - return self; - }, + if (!cy.styleEnabled()) { + return this; + } - removeClass: function removeClass(classes) { - return this.toggleClass(classes, false); - }, + var style = cy.style(); - flashClass: function flashClass(classes, duration) { - var self = this; + properties = util.assign({}, properties, params); - if (duration == null) { - duration = 250; - } else if (duration === 0) { - return self; // nothing to do really - } + var propertiesEmpty = Object.keys(properties).length === 0; - self.addClass(classes); - setTimeout(function () { - self.removeClass(classes); - }, duration); + if (propertiesEmpty) { + return new Animation(all[0], properties); // nothing to animate + } - return self; - } -}; + if (properties.duration === undefined) { + properties.duration = 400; + } -module.exports = elesfn; + switch (properties.duration) { + case 'slow': + properties.duration = 600; + break; + case 'fast': + properties.duration = 200; + break; + } -/***/ }), -/* 38 */ -/***/ (function(module, exports, __webpack_require__) { + if (isEles) { + properties.style = style.getPropsList(properties.style || properties.css); -"use strict"; + properties.css = undefined; + } + if (isEles && properties.renderedPosition != null) { + var rpos = properties.renderedPosition; + var pan = cy.pan(); + var zoom = cy.zoom(); -var is = __webpack_require__(0); -var Selector = __webpack_require__(6); + properties.position = math.renderedToModelPosition(rpos, zoom, pan); + } -var elesfn = { - allAre: function allAre(selector) { - var selObj = new Selector(selector); + // override pan w/ panBy if set + if (isCore && properties.panBy != null) { + var panBy = properties.panBy; + var cyPan = cy.pan(); - return this.every(function (ele) { - return selObj.matches(ele); - }); - }, + properties.pan = { + x: cyPan.x + panBy.x, + y: cyPan.y + panBy.y + }; + } - is: function is(selector) { - var selObj = new Selector(selector); + // override pan w/ center if set + var center = properties.center || properties.centre; + if (isCore && center != null) { + var centerPan = cy.getCenterPan(center.eles, properties.zoom); - return this.some(function (ele) { - return selObj.matches(ele); - }); - }, + if (centerPan != null) { + properties.pan = centerPan; + } + } - some: function some(fn, thisArg) { - for (var i = 0; i < this.length; i++) { - var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]); + // override pan & zoom w/ fit if set + if (isCore && properties.fit != null) { + var fit = properties.fit; + var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding); - if (ret) { - return true; + if (fitVp != null) { + properties.pan = fitVp.pan; + properties.zoom = fitVp.zoom; + } } - } - return false; - }, + // override zoom (& potentially pan) w/ zoom obj if set + if (isCore && is.plainObject(properties.zoom)) { + var vp = cy.getZoomedViewport(properties.zoom); - every: function every(fn, thisArg) { - for (var i = 0; i < this.length; i++) { - var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]); + if (vp != null) { + if (vp.zoomed) { + properties.zoom = vp.zoom; + } - if (!ret) { - return false; + if (vp.panned) { + properties.pan = vp.pan; + } + } } - } - return true; - }, + return new Animation(all[0], properties); + }; + }, // animate - same: function same(collection) { - collection = this.cy().collection(collection); + animate: function animate() { + return function animateImpl(properties, params) { + var self = this; + var selfIsArrayLike = self.length !== undefined; + var all = selfIsArrayLike ? self : [self]; // put in array if not array-like + var cy = this._private.cy || this; - // cheap extra check - if (this.length !== collection.length) { - return false; - } + if (!cy.styleEnabled()) { + return this; + } - return this.every(function (ele) { - return collection.hasElementWithId(ele.id()); - }); - }, + if (params) { + properties = util.extend({}, properties, params); + } - anySame: function anySame(collection) { - collection = this.cy().collection(collection); + // manually hook and run the animation + for (var i = 0; i < all.length; i++) { + var ele = all[i]; + var queue = ele.animated() && (properties.queue === undefined || properties.queue); - return this.some(function (ele) { - return collection.hasElementWithId(ele.id()); - }); - }, + var ani = ele.animation(properties, queue ? { queue: true } : undefined); - allAreNeighbors: function allAreNeighbors(collection) { - collection = this.cy().collection(collection); + ani.play(); + } - var nhood = this.neighborhood(); + return this; // chaining + }; + }, // animate - return collection.every(function (ele) { - return nhood.hasElementWithId(ele.id()); - }); - }, + stop: function stop() { + return function stopImpl(clearQueue, jumpToEnd) { + var self = this; + var selfIsArrayLike = self.length !== undefined; + var all = selfIsArrayLike ? self : [self]; // put in array if not array-like + var cy = this._private.cy || this; - contains: function contains(collection) { - collection = this.cy().collection(collection); + if (!cy.styleEnabled()) { + return this; + } - var self = this; + for (var i = 0; i < all.length; i++) { + var ele = all[i]; + var _p = ele._private; + var anis = _p.animation.current; - return collection.every(function (ele) { - return self.hasElementWithId(ele.id()); - }); - } -}; + for (var j = 0; j < anis.length; j++) { + var ani = anis[j]; + var ani_p = ani._private; -elesfn.allAreNeighbours = elesfn.allAreNeighbors; -elesfn.has = elesfn.contains; + if (jumpToEnd) { + // next iteration of the animation loop, the animation + // will go straight to the end and be removed + ani_p.duration = 0; + } + } -module.exports = elesfn; + // clear the queue of future animations + if (clearQueue) { + _p.animation.queue = []; + } + + if (!jumpToEnd) { + _p.animation.current = []; + } + } + + // we have to notify (the animation loop doesn't do it for us on `stop`) + cy.notify({ + eles: this, + type: 'draw' + }); + + return this; + }; + } // stop + +}; // define + +module.exports = define; /***/ }), -/* 39 */ +/* 44 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Set = __webpack_require__(9); +var util = __webpack_require__(1); +var is = __webpack_require__(0); +var Promise = __webpack_require__(5); -var elesfn = { - parent: function parent(selector) { - var parents = []; +var Animation = function Animation(target, opts, opts2) { + var _p = this._private = util.extend({ + duration: 1000 + }, opts, opts2); - // optimisation for single ele call - if (this.length === 1) { - var parent = this[0]._private.parent; + _p.target = target; + _p.style = _p.style || _p.css; + _p.started = false; + _p.playing = false; + _p.hooked = false; + _p.applying = false; + _p.progress = 0; + _p.completes = []; + _p.frames = []; - if (parent) { - return parent; - } - } + if (_p.complete && is.fn(_p.complete)) { + _p.completes.push(_p.complete); + } - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - var _parent = ele._private.parent; + // for future timeline/animations impl + this.length = 1; + this[0] = this; +}; - if (_parent) { - parents.push(_parent); - } - } +var anifn = Animation.prototype; - return this.spawn(parents, { unique: true }).filter(selector); +util.extend(anifn, { + + instanceString: function instanceString() { + return 'animation'; }, - parents: function parents(selector) { - var parents = []; + hook: function hook() { + var _p = this._private; - var eles = this.parent(); - while (eles.nonempty()) { - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - parents.push(ele); + if (!_p.hooked) { + // add to target's animation queue + var q = void 0; + var tAni = _p.target._private.animation; + if (_p.queue) { + q = tAni.queue; + } else { + q = tAni.current; } + q.push(this); - eles = eles.parent(); + // add to the animation loop pool + if (is.elementOrCollection(_p.target)) { + _p.target.cy().addToAnimationPool(_p.target); + } + + _p.hooked = true; } - return this.spawn(parents, { unique: true }).filter(selector); + return this; }, - commonAncestors: function commonAncestors(selector) { - var ancestors = void 0; + play: function play() { + var _p = this._private; - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - var parents = ele.parents(); + // autorewind + if (_p.progress === 1) { + _p.progress = 0; + } - ancestors = ancestors || parents; + _p.playing = true; + _p.started = false; // needs to be started by animation loop + _p.stopped = false; - ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set - } + this.hook(); - return ancestors.filter(selector); - }, + // the animation loop will start the animation... - orphans: function orphans(selector) { - return this.stdFilter(function (ele) { - return ele.isOrphan(); - }).filter(selector); + return this; }, - nonorphans: function nonorphans(selector) { - return this.stdFilter(function (ele) { - return ele.isChild(); - }).filter(selector); + playing: function playing() { + return this._private.playing; }, - children: function children(selector) { - var children = []; + apply: function apply() { + var _p = this._private; - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - children = children.concat(ele._private.children); - } + _p.applying = true; + _p.started = false; // needs to be started by animation loop + _p.stopped = false; - return this.spawn(children, { unique: true }).filter(selector); + this.hook(); + + // the animation loop will apply the animation at this progress + + return this; }, - siblings: function siblings(selector) { - return this.parent().children().not(this).filter(selector); + applying: function applying() { + return this._private.applying; }, - isParent: function isParent() { - var ele = this[0]; + pause: function pause() { + var _p = this._private; - if (ele) { - return ele.isNode() && ele._private.children.length !== 0; - } + _p.playing = false; + _p.started = false; + + return this; }, - isChildless: function isChildless() { - var ele = this[0]; + stop: function stop() { + var _p = this._private; - if (ele) { - return ele.isNode() && ele._private.children.length === 0; - } + _p.playing = false; + _p.started = false; + _p.stopped = true; // to be removed from animation queues + + return this; }, - isChild: function isChild() { - var ele = this[0]; + rewind: function rewind() { + return this.progress(0); + }, - if (ele) { - return ele.isNode() && ele._private.parent != null; - } + fastforward: function fastforward() { + return this.progress(1); }, - isOrphan: function isOrphan() { - var ele = this[0]; + time: function time(t) { + var _p = this._private; - if (ele) { - return ele.isNode() && ele._private.parent == null; + if (t === undefined) { + return _p.progress * _p.duration; + } else { + return this.progress(t / _p.duration); } }, - descendants: function descendants(selector) { - var elements = []; + progress: function progress(p) { + var _p = this._private; + var wasPlaying = _p.playing; - function add(eles) { - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; + if (p === undefined) { + return _p.progress; + } else { + if (wasPlaying) { + this.pause(); + } - elements.push(ele); + _p.progress = p; + _p.started = false; - if (ele.children().nonempty()) { - add(ele.children()); - } + if (wasPlaying) { + this.play(); } } - add(this.children()); + return this; + }, - return this.spawn(elements, { unique: true }).filter(selector); - } -}; - -function forEachCompound(eles, fn, includeSelf, recursiveStep) { - var q = []; - var did = new Set(); - var cy = eles.cy(); - var hasCompounds = cy.hasCompoundNodes(); + completed: function completed() { + return this._private.progress === 1; + }, - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; + reverse: function reverse() { + var _p = this._private; + var wasPlaying = _p.playing; - if (includeSelf) { - q.push(ele); - } else if (hasCompounds) { - recursiveStep(q, did, ele); + if (wasPlaying) { + this.pause(); } - } - - while (q.length > 0) { - var _ele = q.shift(); - fn(_ele); + _p.progress = 1 - _p.progress; + _p.started = false; - did.add(_ele.id()); + var swap = function swap(a, b) { + var _pa = _p[a]; - if (hasCompounds) { - recursiveStep(q, did, _ele); - } - } + if (_pa == null) { + return; + } - return eles; -} + _p[a] = _p[b]; + _p[b] = _pa; + }; -function addChildren(q, did, ele) { - if (ele.isParent()) { - var children = ele._private.children; + swap('zoom', 'startZoom'); + swap('pan', 'startPan'); + swap('position', 'startPosition'); - for (var i = 0; i < children.length; i++) { - var child = children[i]; + // swap styles + if (_p.style) { + for (var i = 0; i < _p.style.length; i++) { + var prop = _p.style[i]; + var name = prop.name; + var startStyleProp = _p.startStyle[name]; - if (!did.has(child.id())) { - q.push(child); + _p.startStyle[name] = prop; + _p.style[i] = startStyleProp; } } - } -} - -// very efficient version of eles.add( eles.descendants() ).forEach() -// for internal use -elesfn.forEachDown = function (fn) { - var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - - return forEachCompound(this, fn, includeSelf, addChildren); -}; - -function addParent(q, did, ele) { - if (ele.isChild()) { - var parent = ele._private.parent; - if (!did.has(parent.id())) { - q.push(parent); + if (wasPlaying) { + this.play(); } - } -} -elesfn.forEachUp = function (fn) { - var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + return this; + }, - return forEachCompound(this, fn, includeSelf, addParent); -}; + promise: function promise(type) { + var _p = this._private; -function addParentAndChildren(q, did, ele) { - addParent(q, did, ele); - addChildren(q, did, ele); -} + var arr = void 0; -elesfn.forEachUpAndDown = function (fn) { - var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + switch (type) { + case 'frame': + arr = _p.frames; + break; + default: + case 'complete': + case 'completed': + arr = _p.completes; + } - return forEachCompound(this, fn, includeSelf, addParentAndChildren); -}; + return new Promise(function (resolve, reject) { + arr.push(function () { + resolve(); + }); + }); + } -// aliases -elesfn.ancestors = elesfn.parents; +}); -module.exports = elesfn; +anifn.complete = anifn.completed; + +module.exports = Animation; /***/ }), -/* 40 */ +/* 45 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(3); -var fn = void 0, - elesfn = void 0; - -fn = elesfn = { - - data: define.data({ - field: 'data', - bindingEvent: 'data', - allowBinding: true, - allowSetting: true, - settingEvent: 'data', - settingTriggersEvent: true, - triggerFnName: 'trigger', - allowGetting: true, - immutableKeys: { - 'id': true, - 'source': true, - 'target': true, - 'parent': true - }, - updateStyle: true - }), +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - removeData: define.removeData({ - field: 'data', - event: 'data', - triggerFnName: 'trigger', - triggerEvent: true, - immutableKeys: { - 'id': true, - 'source': true, - 'target': true, - 'parent': true - }, - updateStyle: true - }), +var util = __webpack_require__(1); +var is = __webpack_require__(0); - scratch: define.data({ - field: 'scratch', - bindingEvent: 'scratch', - allowBinding: true, - allowSetting: true, - settingEvent: 'scratch', - settingTriggersEvent: true, - triggerFnName: 'trigger', - allowGetting: true, - updateStyle: true - }), +var define = { - removeScratch: define.removeData({ - field: 'scratch', - event: 'scratch', - triggerFnName: 'trigger', - triggerEvent: true, - updateStyle: true - }), + // access data field + data: function data(params) { + var defaults = { + field: 'data', + bindingEvent: 'data', + allowBinding: false, + allowSetting: false, + allowGetting: false, + settingEvent: 'data', + settingTriggersEvent: false, + triggerFnName: 'trigger', + immutableKeys: {}, // key => true if immutable + updateStyle: false, + beforeGet: function beforeGet(self) {}, + beforeSet: function beforeSet(self, obj) {}, + onSet: function onSet(self) {}, + canSet: function canSet(self) { + return true; + } + }; + params = util.extend({}, defaults, params); - rscratch: define.data({ - field: 'rscratch', - allowBinding: false, - allowSetting: true, - settingTriggersEvent: false, - allowGetting: true - }), + return function dataImpl(name, value) { + var p = params; + var self = this; + var selfIsArrayLike = self.length !== undefined; + var all = selfIsArrayLike ? self : [self]; // put in array if not array-like + var single = selfIsArrayLike ? self[0] : self; - removeRscratch: define.removeData({ - field: 'rscratch', - triggerEvent: false - }), + // .data('foo', ...) + if (is.string(name)) { + // set or get property - id: function id() { - var ele = this[0]; + // .data('foo') + if (p.allowGetting && value === undefined) { + // get - if (ele) { - return ele._private.data.id; - } - } + var ret = void 0; + if (single) { + p.beforeGet(single); -}; + ret = single._private[p.field][name]; + } + return ret; -// aliases -fn.attr = fn.data; -fn.removeAttr = fn.removeData; + // .data('foo', 'bar') + } else if (p.allowSetting && value !== undefined) { + // set + var valid = !p.immutableKeys[name]; + if (valid) { + var change = _defineProperty({}, name, value); -module.exports = elesfn; + p.beforeSet(self, change); -/***/ }), -/* 41 */ -/***/ (function(module, exports, __webpack_require__) { + for (var i = 0, l = all.length; i < l; i++) { + var ele = all[i]; -"use strict"; + if (p.canSet(ele)) { + ele._private[p.field][name] = value; + } + } + // update mappers if asked + if (p.updateStyle) { + self.updateStyle(); + } -var util = __webpack_require__(1); + // call onSet callback + p.onSet(self); -var elesfn = {}; + if (p.settingTriggersEvent) { + self[p.triggerFnName](p.settingEvent); + } + } + } -function defineDegreeFunction(callback) { - return function (includeLoops) { - var self = this; + // .data({ 'foo': 'bar' }) + } else if (p.allowSetting && is.plainObject(name)) { + // extend + var obj = name; + var k = void 0, + v = void 0; + var keys = Object.keys(obj); - if (includeLoops === undefined) { - includeLoops = true; - } + p.beforeSet(self, obj); - if (self.length === 0) { - return; - } + for (var _i = 0; _i < keys.length; _i++) { + k = keys[_i]; + v = obj[k]; - if (self.isNode() && !self.removed()) { - var degree = 0; - var node = self[0]; - var connectedEdges = node._private.edges; + var _valid = !p.immutableKeys[k]; + if (_valid) { + for (var j = 0; j < all.length; j++) { + var _ele = all[j]; - for (var i = 0; i < connectedEdges.length; i++) { - var edge = connectedEdges[i]; + if (p.canSet(_ele)) { + _ele._private[p.field][k] = v; + } + } + } + } - if (!includeLoops && edge.isLoop()) { - continue; + // update mappers if asked + if (p.updateStyle) { + self.updateStyle(); } - degree += callback(node, edge); - } + // call onSet callback + p.onSet(self); - return degree; - } else { - return; - } - }; -} + if (p.settingTriggersEvent) { + self[p.triggerFnName](p.settingEvent); + } -util.extend(elesfn, { - degree: defineDegreeFunction(function (node, edge) { - if (edge.source().same(edge.target())) { - return 2; - } else { - return 1; - } - }), + // .data(function(){ ... }) + } else if (p.allowBinding && is.fn(name)) { + // bind to event + var fn = name; + self.on(p.bindingEvent, fn); - indegree: defineDegreeFunction(function (node, edge) { - if (edge.target().same(node)) { - return 1; - } else { - return 0; - } - }), + // .data() + } else if (p.allowGetting && name === undefined) { + // get whole object + var _ret = void 0; + if (single) { + p.beforeGet(single); - outdegree: defineDegreeFunction(function (node, edge) { - if (edge.source().same(node)) { - return 1; - } else { - return 0; - } - }) -}); + _ret = single._private[p.field]; + } + return _ret; + } -function defineDegreeBoundsFunction(degreeFn, callback) { - return function (includeLoops) { - var ret = void 0; - var nodes = this.nodes(); + return self; // maintain chainability + }; // function + }, // data - for (var i = 0; i < nodes.length; i++) { - var ele = nodes[i]; - var degree = ele[degreeFn](includeLoops); - if (degree !== undefined && (ret === undefined || callback(degree, ret))) { - ret = degree; - } - } + // remove data field + removeData: function removeData(params) { + var defaults = { + field: 'data', + event: 'data', + triggerFnName: 'trigger', + triggerEvent: false, + immutableKeys: {} // key => true if immutable + }; + params = util.extend({}, defaults, params); - return ret; - }; -} + return function removeDataImpl(names) { + var p = params; + var self = this; + var selfIsArrayLike = self.length !== undefined; + var all = selfIsArrayLike ? self : [self]; // put in array if not array-like -util.extend(elesfn, { - minDegree: defineDegreeBoundsFunction('degree', function (degree, min) { - return degree < min; - }), + // .removeData('foo bar') + if (is.string(names)) { + // then get the list of keys, and delete them + var keys = names.split(/\s+/); + var l = keys.length; - maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) { - return degree > max; - }), + for (var i = 0; i < l; i++) { + // delete each non-empty key + var key = keys[i]; + if (is.emptyString(key)) { + continue; + } - minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) { - return degree < min; - }), + var valid = !p.immutableKeys[key]; // not valid if immutable + if (valid) { + for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) { + all[i_a]._private[p.field][key] = undefined; + } + } + } - maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) { - return degree > max; - }), + if (p.triggerEvent) { + self[p.triggerFnName](p.event); + } - minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) { - return degree < min; - }), + // .removeData() + } else if (names === undefined) { + // then delete all keys - maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) { - return degree > max; - }) -}); + for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) { + var _privateFields = all[_i_a]._private[p.field]; + var _keys = Object.keys(_privateFields); -util.extend(elesfn, { - totalDegree: function totalDegree(includeLoops) { - var total = 0; - var nodes = this.nodes(); + for (var _i2 = 0; _i2 < _keys.length; _i2++) { + var _key = _keys[_i2]; + var validKeyToDelete = !p.immutableKeys[_key]; - for (var i = 0; i < nodes.length; i++) { - total += nodes[i].degree(includeLoops); - } + if (validKeyToDelete) { + _privateFields[_key] = undefined; + } + } + } - return total; - } -}); + if (p.triggerEvent) { + self[p.triggerFnName](p.event); + } + } -module.exports = elesfn; + return self; // maintain chaining + }; // function + } // removeData +}; // define + +module.exports = define; /***/ }), -/* 42 */ +/* 46 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var is = __webpack_require__(0); -var util = __webpack_require__(1); -var math = __webpack_require__(2); -var fn = void 0, - elesfn = void 0; +var Promise = __webpack_require__(5); -fn = elesfn = {}; +var define = { -elesfn.renderedBoundingBox = function (options) { - var bb = this.boundingBox(options); - var cy = this.cy(); - var zoom = cy.zoom(); - var pan = cy.pan(); + eventAliasesOn: function eventAliasesOn(proto) { + var p = proto; - var x1 = bb.x1 * zoom + pan.x; - var x2 = bb.x2 * zoom + pan.x; - var y1 = bb.y1 * zoom + pan.y; - var y2 = bb.y2 * zoom + pan.y; + p.addListener = p.listen = p.bind = p.on; + p.unlisten = p.unbind = p.off = p.removeListener; + p.trigger = p.emit; - return { - x1: x1, - x2: x2, - y1: y1, - y2: y2, - w: x2 - x1, - h: y2 - y1 - }; -}; + // this is just a wrapper alias of .on() + p.pon = p.promiseOn = function (events, selector) { + var self = this; + var args = Array.prototype.slice.call(arguments, 0); -elesfn.dirtyCompoundBoundsCache = function () { - var cy = this.cy(); + return new Promise(function (resolve, reject) { + var callback = function callback(e) { + self.off.apply(self, offArgs); - if (!cy.styleEnabled() || !cy.hasCompoundNodes()) { - return this; - } - - this.forEachUp(function (ele) { - ele._private.compoundBoundsClean = false; + resolve(e); + }; - if (ele.isParent()) { - ele.emit('bounds'); - } - }); + var onArgs = args.concat([callback]); + var offArgs = onArgs.concat([]); - return this; -}; + self.on.apply(self, onArgs); + }); + }; + } -elesfn.updateCompoundBounds = function () { - var cy = this.cy(); +}; // define - // save cycles for non compound graphs or when style disabled - if (!cy.styleEnabled() || !cy.hasCompoundNodes()) { - return this; - } +module.exports = define; - // save cycles when batching -- but bounds will be stale (or not exist yet) - if (cy.batching()) { - return this; - } +/***/ }), +/* 47 */ +/***/ (function(module, exports, __webpack_require__) { - var updated = []; +"use strict"; - function update(parent) { - if (!parent.isParent()) { - return; - } - var _p = parent._private; - var children = parent.children(); - var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include'; +var Set = __webpack_require__(8); - var min = { - width: { - val: parent.pstyle('min-width').pfValue, - left: parent.pstyle('min-width-bias-left'), - right: parent.pstyle('min-width-bias-right') - }, - height: { - val: parent.pstyle('min-height').pfValue, - top: parent.pstyle('min-height-bias-top'), - bottom: parent.pstyle('min-height-bias-bottom') - } - }; +var elesfn = { + classes: function classes(_classes) { + _classes = (_classes || '').match(/\S+/g) || []; + var self = this; + var changed = []; + var classesMap = new Set(_classes); - var bb = children.boundingBox({ - includeLabels: includeLabels, - includeOverlays: false, + // check and update each ele - // updating the compound bounds happens outside of the regular - // cache cycle (i.e. before fired events) - useCache: false - }); - var pos = _p.position; + var _loop = function _loop(j) { + var ele = self[j]; + var _p = ele._private; + var eleClasses = _p.classes; + var changedEle = false; - // if children take up zero area then keep position and fall back on stylesheet w/h - if (bb.w === 0 || bb.h === 0) { - bb = { - w: parent.pstyle('width').pfValue, - h: parent.pstyle('height').pfValue - }; + // check if ele has all of the passed classes + classesMap.forEach(function (cls) { + var eleHasClass = eleClasses.has(cls); - bb.x1 = pos.x - bb.w / 2; - bb.x2 = pos.x + bb.w / 2; - bb.y1 = pos.y - bb.h / 2; - bb.y2 = pos.y + bb.h / 2; - } + if (!eleHasClass) { + changedEle = true; + } + }); - function computeBiasValues(propDiff, propBias, propBiasComplement) { - var biasDiff = 0; - var biasComplementDiff = 0; - var biasTotal = propBias + propBiasComplement; + // check if ele has classes outside of those passed + if (!changedEle) { + eleClasses.forEach(function (eleCls) { + var specdClass = classesMap.has(eleCls); - if (propDiff > 0 && biasTotal > 0) { - biasDiff = propBias / biasTotal * propDiff; - biasComplementDiff = propBiasComplement / biasTotal * propDiff; + if (!specdClass) { + changedEle = true; + } + }); } - return { - biasDiff: biasDiff, - biasComplementDiff: biasComplementDiff - }; - } - function computePaddingValues(width, height, paddingObject, relativeTo) { - // Assuming percentage is number from 0 to 1 - if (paddingObject.units === '%') { - switch (relativeTo) { - case 'width': - return width > 0 ? paddingObject.pfValue * width : 0; - case 'height': - return height > 0 ? paddingObject.pfValue * height : 0; - case 'average': - return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0; - case 'min': - return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0; - case 'max': - return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0; - default: - return 0; - } - } else if (paddingObject.units === 'px') { - return paddingObject.pfValue; - } else { - return 0; + if (changedEle) { + _p.classes = new Set(classesMap); + + changed.push(ele); } - } + }; - var leftVal = min.width.left.value; - if (min.width.left.units === 'px' && min.width.val > 0) { - leftVal = leftVal * 100 / min.width.val; - } - var rightVal = min.width.right.value; - if (min.width.right.units === 'px' && min.width.val > 0) { - rightVal = rightVal * 100 / min.width.val; + for (var j = 0; j < self.length; j++) { + _loop(j); } - var topVal = min.height.top.value; - if (min.height.top.units === 'px' && min.height.val > 0) { - topVal = topVal * 100 / min.height.val; + // trigger update style on those eles that had class changes + if (changed.length > 0) { + this.spawn(changed).updateStyle().emit('class'); } - var bottomVal = min.height.bottom.value; - if (min.height.bottom.units === 'px' && min.height.val > 0) { - bottomVal = bottomVal * 100 / min.height.val; - } + return self; + }, - var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal); - var diffLeft = widthBiasDiffs.biasDiff; - var diffRight = widthBiasDiffs.biasComplementDiff; + addClass: function addClass(classes) { + return this.toggleClass(classes, true); + }, - var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal); - var diffTop = heightBiasDiffs.biasDiff; - var diffBottom = heightBiasDiffs.biasComplementDiff; + hasClass: function hasClass(className) { + var ele = this[0]; + return ele != null && ele._private.classes.has(className); + }, - _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value); + toggleClass: function toggleClass(classesStr, toggle) { + var classes = classesStr.match(/\S+/g) || []; + var self = this; + var changed = []; // eles who had classes changed - _p.autoWidth = Math.max(bb.w, min.width.val); - pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2; + for (var i = 0, il = self.length; i < il; i++) { + var _ele = self[i]; + var _changedEle = false; - _p.autoHeight = Math.max(bb.h, min.height.val); - pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2; + for (var j = 0; j < classes.length; j++) { + var cls = classes[j]; + var _eleClasses = _ele._private.classes; + var hasClass = _eleClasses.has(cls); + var shouldAdd = toggle || toggle === undefined && !hasClass; - updated.push(parent); - } + if (shouldAdd) { + _eleClasses.add(cls); - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - var _p = ele._private; + if (!hasClass && !_changedEle) { + changed.push(_ele); + _changedEle = true; + } + } else { + // then remove + _eleClasses.delete(cls); - if (!_p.compoundBoundsClean) { - update(ele); + if (hasClass && !_changedEle) { + changed.push(_ele); + _changedEle = true; + } + } + } // for j classes + } // for i eles - if (!cy._private.batchingStyle) { - _p.compoundBoundsClean = true; - } + // trigger update style on those eles that had class changes + if (changed.length > 0) { + this.spawn(changed).updateStyle().emit('class'); } - } - return this; -}; + return self; + }, -var noninf = function noninf(x) { - if (x === Infinity || x === -Infinity) { - return 0; - } + removeClass: function removeClass(classes) { + return this.toggleClass(classes, false); + }, - return x; -}; + flashClass: function flashClass(classes, duration) { + var self = this; -var updateBounds = function updateBounds(b, x1, y1, x2, y2) { - // don't update with zero area boxes - if (x2 - x1 === 0 || y2 - y1 === 0) { - return; - } + if (duration == null) { + duration = 250; + } else if (duration === 0) { + return self; // nothing to do really + } - // don't update with null dim - if (x1 == null || y1 == null || x2 == null || y2 == null) { - return; + self.addClass(classes); + setTimeout(function () { + self.removeClass(classes); + }, duration); + + return self; } +}; - b.x1 = x1 < b.x1 ? x1 : b.x1; - b.x2 = x2 > b.x2 ? x2 : b.x2; - b.y1 = y1 < b.y1 ? y1 : b.y1; - b.y2 = y2 > b.y2 ? y2 : b.y2; -}; - -var updateBoundsFromBox = function updateBoundsFromBox(b, b2) { - return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2); -}; - -var prefixedProperty = function prefixedProperty(obj, field, prefix) { - return util.getPrefixedProperty(obj, field, prefix); -}; +module.exports = elesfn; -var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) { - if (ele.cy().headless()) { - return; - } +/***/ }), +/* 48 */ +/***/ (function(module, exports, __webpack_require__) { - var _p = ele._private; - var rstyle = _p.rstyle; - var halfArW = rstyle.arrowWidth / 2; - var arrowType = ele.pstyle(prefix + '-arrow-shape').value; - var x = void 0; - var y = void 0; +"use strict"; - if (arrowType !== 'none') { - if (prefix === 'source') { - x = rstyle.srcX; - y = rstyle.srcY; - } else if (prefix === 'target') { - x = rstyle.tgtX; - y = rstyle.tgtY; - } else { - x = rstyle.midX; - y = rstyle.midY; - } - updateBounds(bounds, x - halfArW, y - halfArW, x + halfArW, y + halfArW); - } -}; +var is = __webpack_require__(0); +var Selector = __webpack_require__(6); -var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) { - if (ele.cy().headless()) { - return; - } +var elesfn = { + allAre: function allAre(selector) { + var selObj = new Selector(selector); - var prefixDash = void 0; + return this.every(function (ele) { + return selObj.matches(ele); + }); + }, - if (prefix) { - prefixDash = prefix + '-'; - } else { - prefixDash = ''; - } + is: function is(selector) { + var selObj = new Selector(selector); - var _p = ele._private; - var rstyle = _p.rstyle; - var label = ele.pstyle(prefixDash + 'label').strValue; + return this.some(function (ele) { + return selObj.matches(ele); + }); + }, - if (label) { - var halign = ele.pstyle('text-halign'); - var valign = ele.pstyle('text-valign'); - var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix); - var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix); - var labelX = prefixedProperty(rstyle, 'labelX', prefix); - var labelY = prefixedProperty(rstyle, 'labelY', prefix); - var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue; - var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue; - var isEdge = ele.isEdge(); - var rotation = ele.pstyle(prefixDash + 'text-rotation'); - var outlineWidth = ele.pstyle('text-outline-width').pfValue; - var borderWidth = ele.pstyle('text-border-width').pfValue; - var halfBorderWidth = borderWidth / 2; - var padding = ele.pstyle('text-background-padding').pfValue; + some: function some(fn, thisArg) { + for (var i = 0; i < this.length; i++) { + var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]); - var lh = labelHeight + 2 * padding; - var lw = labelWidth + 2 * padding; - var lw_2 = lw / 2; - var lh_2 = lh / 2; - var lx1 = void 0, - lx2 = void 0, - ly1 = void 0, - ly2 = void 0; + if (ret) { + return true; + } + } - if (isEdge) { - lx1 = labelX - lw_2; - lx2 = labelX + lw_2; - ly1 = labelY - lh_2; - ly2 = labelY + lh_2; - } else { - switch (halign.value) { - case 'left': - lx1 = labelX - lw; - lx2 = labelX; - break; + return false; + }, - case 'center': - lx1 = labelX - lw_2; - lx2 = labelX + lw_2; - break; + every: function every(fn, thisArg) { + for (var i = 0; i < this.length; i++) { + var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]); - case 'right': - lx1 = labelX; - lx2 = labelX + lw; - break; + if (!ret) { + return false; } + } - switch (valign.value) { - case 'top': - ly1 = labelY - lh; - ly2 = labelY; - break; + return true; + }, - case 'center': - ly1 = labelY - lh_2; - ly2 = labelY + lh_2; - break; + same: function same(collection) { + collection = this.cy().collection(collection); - case 'bottom': - ly1 = labelY; - ly2 = labelY + lh; - break; - } + // cheap extra check + if (this.length !== collection.length) { + return false; } - var isAutorotate = isEdge && rotation.strValue === 'autorotate'; - var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0; + return this.every(function (ele) { + return collection.hasElementWithId(ele.id()); + }); + }, - if (isAutorotate || isPfValue) { - var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue; - var cos = Math.cos(theta); - var sin = Math.sin(theta); + anySame: function anySame(collection) { + collection = this.cy().collection(collection); - var rotate = function rotate(x, y) { - x = x - labelX; - y = y - labelY; + return this.some(function (ele) { + return collection.hasElementWithId(ele.id()); + }); + }, - return { - x: x * cos - y * sin + labelX, - y: x * sin + y * cos + labelY - }; - }; + allAreNeighbors: function allAreNeighbors(collection) { + collection = this.cy().collection(collection); - var px1y1 = rotate(lx1, ly1); - var px1y2 = rotate(lx1, ly2); - var px2y1 = rotate(lx2, ly1); - var px2y2 = rotate(lx2, ly2); + var nhood = this.neighborhood(); - lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x); - lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x); - ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y); - ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y); - } + return collection.every(function (ele) { + return nhood.hasElementWithId(ele.id()); + }); + }, - lx1 += marginX - Math.max(outlineWidth, halfBorderWidth); - lx2 += marginX + Math.max(outlineWidth, halfBorderWidth); - ly1 += marginY - Math.max(outlineWidth, halfBorderWidth); - ly2 += marginY + Math.max(outlineWidth, halfBorderWidth); + contains: function contains(collection) { + collection = this.cy().collection(collection); - updateBounds(bounds, lx1, ly1, lx2, ly2); - } + var self = this; - return bounds; + return collection.every(function (ele) { + return self.hasElementWithId(ele.id()); + }); + } }; -// get the bounding box of the elements (in raw model position) -var boundingBoxImpl = function boundingBoxImpl(ele, options) { - var cy = ele._private.cy; - var styleEnabled = cy.styleEnabled(); - var headless = cy.headless(); +elesfn.allAreNeighbours = elesfn.allAreNeighbors; +elesfn.has = elesfn.contains; - var bounds = { - x1: Infinity, - y1: Infinity, - x2: -Infinity, - y2: -Infinity - }; +module.exports = elesfn; - var _p = ele._private; - var display = styleEnabled ? ele.pstyle('display').value : 'element'; - var isNode = ele.isNode(); - var isEdge = ele.isEdge(); - var ex1 = void 0, - ex2 = void 0, - ey1 = void 0, - ey2 = void 0; // extrema of body / lines - var x = void 0, - y = void 0; // node pos - var displayed = display !== 'none'; +/***/ }), +/* 49 */ +/***/ (function(module, exports, __webpack_require__) { - if (displayed) { - var overlayOpacity = 0; - var overlayPadding = 0; +"use strict"; - if (styleEnabled && options.includeOverlays) { - overlayOpacity = ele.pstyle('overlay-opacity').value; - if (overlayOpacity !== 0) { - overlayPadding = ele.pstyle('overlay-padding').value; - } - } +var util = __webpack_require__(1); +var exprs = __webpack_require__(50); +var newQuery = __webpack_require__(10); - var w = 0; - var wHalf = 0; +// of all the expressions, find the first match in the remaining text +var consumeExpr = function consumeExpr(remaining) { + var expr = void 0; + var match = void 0; + var name = void 0; - if (styleEnabled) { - w = ele.pstyle('width').pfValue; - wHalf = w / 2; - } + for (var j = 0; j < exprs.length; j++) { + var e = exprs[j]; + var n = e.name; - if (isNode && options.includeNodes) { - var pos = ele.position(); - x = pos.x; - y = pos.y; - var _w = ele.outerWidth(); - var halfW = _w / 2; - var h = ele.outerHeight(); - var halfH = h / 2; - - // handle node dimensions - ///////////////////////// - - ex1 = x - halfW - overlayPadding; - ex2 = x + halfW + overlayPadding; - ey1 = y - halfH - overlayPadding; - ey2 = y + halfH + overlayPadding; - - updateBounds(bounds, ex1, ey1, ex2, ey2); - } else if (isEdge && options.includeEdges) { - var rstyle = _p.rstyle || {}; - - // handle edge dimensions (rough box estimate) - ////////////////////////////////////////////// - if (styleEnabled && !headless) { - ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX); - ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX); - ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY); - ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); - - // take into account edge width - ex1 -= wHalf; - ex2 += wHalf; - ey1 -= wHalf; - ey2 += wHalf; + var m = remaining.match(e.regexObj); - updateBounds(bounds, ex1, ey1, ex2, ey2); - } + if (m != null) { + match = m; + expr = e; + name = n; - // precise haystacks - //////////////////// - if (styleEnabled && !headless && ele.pstyle('curve-style').strValue === 'haystack') { - var hpts = rstyle.haystackPts || []; + var consumed = m[0]; + remaining = remaining.substring(consumed.length); - ex1 = hpts[0].x; - ey1 = hpts[0].y; - ex2 = hpts[1].x; - ey2 = hpts[1].y; + break; // we've consumed one expr, so we can return now + } + } - if (ex1 > ex2) { - var temp = ex1; - ex1 = ex2; - ex2 = temp; - } + return { + expr: expr, + match: match, + name: name, + remaining: remaining + }; +}; - if (ey1 > ey2) { - var _temp = ey1; - ey1 = ey2; - ey2 = _temp; - } +// consume all leading whitespace +var consumeWhitespace = function consumeWhitespace(remaining) { + var match = remaining.match(/^\s+/); - updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf); + if (match) { + var consumed = match[0]; + remaining = remaining.substring(consumed.length); + } - // handle points along edge - /////////////////////////// - } else { - var pts = rstyle.bezierPts || rstyle.linePts || []; + return remaining; +}; - for (var j = 0; j < pts.length; j++) { - var pt = pts[j]; +var parse = function parse(selector) { + var self = this; - ex1 = pt.x - wHalf; - ex2 = pt.x + wHalf; - ey1 = pt.y - wHalf; - ey2 = pt.y + wHalf; + var remaining = self._private.selectorText = selector; - updateBounds(bounds, ex1, ey1, ex2, ey2); - } + var currentQuery = self[0] = newQuery(); + self.length = 1; - // fallback on source and target positions - ////////////////////////////////////////// - if (pts.length === 0) { - var n1 = ele.source(); - var n1pos = n1.position(); + remaining = consumeWhitespace(remaining); // get rid of leading whitespace - var n2 = ele.target(); - var n2pos = n2.position(); + for (;;) { + var check = consumeExpr(remaining); - ex1 = n1pos.x; - ex2 = n2pos.x; - ey1 = n1pos.y; - ey2 = n2pos.y; + if (check.expr == null) { + util.error('The selector `' + selector + '`is invalid'); + return false; + } else { + var args = check.match.slice(1); - if (ex1 > ex2) { - var _temp2 = ex1; - ex1 = ex2; - ex2 = _temp2; - } + // let the token populate the selector object in currentQuery + var ret = check.expr.populate(self, currentQuery, args); - if (ey1 > ey2) { - var _temp3 = ey1; - ey1 = ey2; - ey2 = _temp3; - } + if (ret === false) { + return false; // exit if population failed + } else if (ret != null) { + currentQuery = ret; // change the current query to be filled if the expr specifies + } + } - // take into account edge width - ex1 -= wHalf; - ex2 += wHalf; - ey1 -= wHalf; - ey2 += wHalf; + remaining = check.remaining; - updateBounds(bounds, ex1, ey1, ex2, ey2); - } - } - } // edges + // we're done when there's nothing left to parse + if (remaining.match(/^\s*$/)) { + break; + } + } + // adjust references for subject + for (var j = 0; j < self.length; j++) { + var query = self[j]; - // handle edge arrow size - ///////////////////////// + if (query.subject != null) { + // go up the tree until we reach the subject + for (;;) { + if (query.subject === query) { + break; + } // done if subject is self - if (styleEnabled && options.includeEdges && isEdge) { - updateBoundsFromArrow(bounds, ele, 'mid-source', options); - updateBoundsFromArrow(bounds, ele, 'mid-target', options); - updateBoundsFromArrow(bounds, ele, 'source', options); - updateBoundsFromArrow(bounds, ele, 'target', options); - } + if (query.parent != null) { + // swap parent/child reference + var parent = query.parent; + var child = query; - // ghost - //////// + child.parent = null; + parent.child = child; - if (styleEnabled) { - var ghost = ele.pstyle('ghost').value === 'yes'; + query = parent; // go up the tree + } else if (query.ancestor != null) { + // swap ancestor/descendant + var ancestor = query.ancestor; + var descendant = query; - if (ghost) { - var gx = ele.pstyle('ghost-offset-x').pfValue; - var gy = ele.pstyle('ghost-offset-y').pfValue; + descendant.ancestor = null; + ancestor.descendant = descendant; - updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy); - } - } + query = ancestor; // go up the tree + } else if (query.source || query.target || query.connectedNodes) { + util.error('The selector `' + self.text() + '` can not contain a subject selector that applies to the source or target of an edge selector'); + return false; + } else { + util.error('When adjusting references for the selector `' + self.text() + '`, neither parent nor ancestor was found'); + return false; + } + } // for - // overlay - ////////// + self[j] = query.subject; // subject should be the root query + } // if + } // for - if (styleEnabled) { + return true; // success +}; - ex1 = bounds.x1; - ex2 = bounds.x2; - ey1 = bounds.y1; - ey2 = bounds.y2; +module.exports = { parse: parse }; - updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding); - } +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { - // handle label dimensions - ////////////////////////// +"use strict"; - if (styleEnabled && options.includeLabels) { - updateBoundsFromLabel(bounds, ele, null, options); - if (isEdge) { - updateBoundsFromLabel(bounds, ele, 'source', options); - updateBoundsFromLabel(bounds, ele, 'target', options); - } - } // style enabled for labels - } // if displayed +var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); - bounds.x1 = noninf(bounds.x1); - bounds.y1 = noninf(bounds.y1); - bounds.x2 = noninf(bounds.x2); - bounds.y2 = noninf(bounds.y2); - bounds.w = noninf(bounds.x2 - bounds.x1); - bounds.h = noninf(bounds.y2 - bounds.y1); +var _require = __webpack_require__(15), + stateSelectorRegex = _require.stateSelectorRegex; - // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides - if (bounds.w > 0 && bounds.h > 0 && displayed) { - math.expandBoundingBox(bounds, 1); - } +var tokens = __webpack_require__(51); +var util = __webpack_require__(1); +var newQuery = __webpack_require__(10); - return bounds; +// when a token like a variable has escaped meta characters, we need to clean the backslashes out +// so that values get compared properly in Selector.filter() +var cleanMetaChars = function cleanMetaChars(str) { + return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) { + return $1; + }); }; -var tf = function tf(val) { - if (val) { - return 't'; - } else { - return 'f'; +var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) { + if (examiningQuery === selector[selector.length - 1]) { + selector[selector.length - 1] = replacementQuery; } }; -var getKey = function getKey(opts) { - var key = ''; - - key += tf(opts.incudeNodes); - key += tf(opts.includeEdges); - key += tf(opts.includeLabels); - key += tf(opts.includeOverlays); - - return key; -}; - -var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) { - var _p = ele._private; - var bb = void 0; - var headless = ele.cy().headless(); - var key = opts === defBbOpts ? defBbOptsKey : getKey(opts); +// NOTE: add new expression syntax here to have it recognised by the parser; +// - a query contains all adjacent (i.e. no separator in between) expressions; +// - the current query is stored in selector[i] --- you can use the reference to `this` in the populate function; +// - you need to check the query objects in Selector.filter() for it actually filter properly, but that's pretty straight forward +// - when you add something here, also add to Selector.toString() +var exprs = [{ + name: 'group', + query: true, + regex: '(' + tokens.group + ')', + populate: function populate(selector, query, _ref) { + var _ref2 = _slicedToArray(_ref, 1), + group = _ref2[0]; - if (!opts.useCache || headless || !_p.bbCache || !_p.bbCache[key]) { - bb = boundingBoxImpl(ele, opts); + query.group = group === '*' ? group : group + 's'; + } +}, { + name: 'state', + query: true, + regex: stateSelectorRegex, + populate: function populate(selector, query, _ref3) { + var _ref4 = _slicedToArray(_ref3, 1), + state = _ref4[0]; - if (!headless) { - _p.bbCache = _p.bbCache || {}; - _p.bbCache[key] = bb; - } - } else { - bb = _p.bbCache[key]; + query.colonSelectors.push(state); } +}, { + name: 'id', + query: true, + regex: '\\#(' + tokens.id + ')', + populate: function populate(selector, query, _ref5) { + var _ref6 = _slicedToArray(_ref5, 1), + id = _ref6[0]; - return bb; -}; + query.ids.push(cleanMetaChars(id)); + } +}, { + name: 'className', + query: true, + regex: '\\.(' + tokens.className + ')', + populate: function populate(selector, query, _ref7) { + var _ref8 = _slicedToArray(_ref7, 1), + className = _ref8[0]; -var defBbOpts = { - includeNodes: true, - includeEdges: true, - includeLabels: true, - includeOverlays: true, - useCache: true -}; + query.classes.push(cleanMetaChars(className)); + } +}, { + name: 'dataExists', + query: true, + regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]', + populate: function populate(selector, query, _ref9) { + var _ref10 = _slicedToArray(_ref9, 1), + variable = _ref10[0]; -var defBbOptsKey = getKey(defBbOpts); + query.data.push({ + field: cleanMetaChars(variable) + }); + } +}, { + name: 'dataCompare', + query: true, + regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]', + populate: function populate(selector, query, _ref11) { + var _ref12 = _slicedToArray(_ref11, 3), + variable = _ref12[0], + comparatorOp = _ref12[1], + value = _ref12[2]; -function filledBbOpts(options) { - return { - includeNodes: util.default(options.includeNodes, defBbOpts.includeNodes), - includeEdges: util.default(options.includeEdges, defBbOpts.includeEdges), - includeLabels: util.default(options.includeLabels, defBbOpts.includeLabels), - includeOverlays: util.default(options.includeOverlays, defBbOpts.includeOverlays), - useCache: util.default(options.useCache, defBbOpts.useCache) - }; -} + var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null; -elesfn.boundingBox = function (options) { - // the main usecase is ele.boundingBox() for a single element with no/def options - // specified s.t. the cache is used, so check for this case to make it faster by - // avoiding the overhead of the rest of the function - if (this.length === 1 && this[0]._private.bbCache && (options === undefined || options.useCache === undefined || options.useCache === true)) { - if (options === undefined) { - options = defBbOpts; + if (valueIsString) { + value = value.substring(1, value.length - 1); } else { - options = filledBbOpts(options); + value = parseFloat(value); } - return cachedBoundingBoxImpl(this[0], options); + query.data.push({ + field: cleanMetaChars(variable), + operator: comparatorOp, + value: value + }); + } +}, { + name: 'dataBool', + query: true, + regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]', + populate: function populate(selector, query, _ref13) { + var _ref14 = _slicedToArray(_ref13, 2), + boolOp = _ref14[0], + variable = _ref14[1]; + + query.data.push({ + field: cleanMetaChars(variable), + operator: boolOp + }); } +}, { + name: 'metaCompare', + query: true, + regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]', + populate: function populate(selector, query, _ref15) { + var _ref16 = _slicedToArray(_ref15, 3), + meta = _ref16[0], + comparatorOp = _ref16[1], + number = _ref16[2]; - var bounds = { - x1: Infinity, - y1: Infinity, - x2: -Infinity, - y2: -Infinity - }; + query.meta.push({ + field: cleanMetaChars(meta), + operator: comparatorOp, + value: parseFloat(number) + }); + } +}, { + name: 'nextQuery', + separator: true, + regex: tokens.separator, + populate: function populate(selector) { + // go on to next query + var nextQuery = selector[selector.length++] = newQuery(); + selector.currentSubject = null; - options = options || util.staticEmptyObject(); + return nextQuery; + } +}, { + name: 'directedEdge', + separator: true, + regex: tokens.directedEdge, + populate: function populate(selector, query) { + var edgeQuery = newQuery(); + var source = query; + var target = newQuery(); - var opts = filledBbOpts(options); + edgeQuery.group = 'edges'; + edgeQuery.target = target; + edgeQuery.source = source; + edgeQuery.subject = selector.currentSubject; - var eles = this; - var cy = eles.cy(); - var styleEnabled = cy.styleEnabled(); + // the query in the selector should be the edge rather than the source + replaceLastQuery(selector, query, edgeQuery); - if (styleEnabled) { - this.recalculateRenderedStyle(opts.useCache); + // we're now populating the target query with expressions that follow + return target; } +}, { + name: 'undirectedEdge', + separator: true, + regex: tokens.undirectedEdge, + populate: function populate(selector, query) { + var edgeQuery = newQuery(); + var source = query; + var target = newQuery(); - this.updateCompoundBounds(); + edgeQuery.group = 'edges'; + edgeQuery.connectedNodes = [source, target]; + edgeQuery.subject = selector.currentSubject; - var updatedEdge = {}; // use to avoid duplicated edge updates + // the query in the selector should be the edge rather than the source + replaceLastQuery(selector, query, edgeQuery); - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; + // we're now populating the target query with expressions that follow + return target; + } +}, { + name: 'child', + separator: true, + regex: tokens.child, + populate: function populate(selector, query) { + // this query is the parent of the following query + var childQuery = newQuery(); + childQuery.parent = query; + childQuery.subject = selector.currentSubject; - if (styleEnabled && ele.isEdge() && ele.pstyle('curve-style').strValue === 'bezier' && !updatedEdge[ele.id()]) { - var edges = ele.parallelEdges(); + // it's cheaper to compare children first and go up so replace the parent + replaceLastQuery(selector, query, childQuery); - for (var j = 0; j < edges.length; j++) { - // make all as updated - updatedEdge[edges[j].id()] = true; - } + // we're now populating the child query with expressions that follow + return childQuery; + } +}, { + name: 'descendant', + separator: true, + regex: tokens.descendant, + populate: function populate(selector, query) { + // this query is the ancestor of the following query + var descendantQuery = newQuery(); + descendantQuery.ancestor = query; + descendantQuery.subject = selector.currentSubject; - edges.recalculateRenderedStyle(opts.useCache); // n.b. ele.parallelEdges() single is cached - } + // it's cheaper to compare descendants first and go up so replace the ancestor + replaceLastQuery(selector, query, descendantQuery); - updateBoundsFromBox(bounds, cachedBoundingBoxImpl(ele, opts)); + // we're now populating the descendant query with expressions that follow + return descendantQuery; } +}, { + name: 'subject', + modifier: true, + regex: tokens.subject, + populate: function populate(selector, query) { + if (selector.currentSubject != null && query.subject != query) { + util.error('Redefinition of subject in selector `' + selector.toString() + '`'); + return false; + } - bounds.x1 = noninf(bounds.x1); - bounds.y1 = noninf(bounds.y1); - bounds.x2 = noninf(bounds.x2); - bounds.y2 = noninf(bounds.y2); - bounds.w = noninf(bounds.x2 - bounds.x1); - bounds.h = noninf(bounds.y2 - bounds.y1); - - return bounds; -}; - -// private helper to get bounding box for custom node positions -// - good for perf in certain cases but currently requires dirtying the rendered style -// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer... -// - try to use for only things like discrete layouts where the node position would change anyway -elesfn.boundingBoxAt = function (fn) { - var nodes = this.nodes(); - - if (is.plainObject(fn)) { - var obj = fn; - - fn = function fn() { - return obj; - }; + selector.currentSubject = query; + query.subject = query; + selector[selector.length - 1].subject = query; } +}]; - // save the current position and set the new one, per node - for (var i = 0; i < nodes.length; i++) { - var n = nodes[i]; - var _p = n._private; - var pos = _p.position; - var newPos = fn.call(n, n, i); +exprs.forEach(function (e) { + return e.regexObj = new RegExp('^' + e.regex); +}); - _p.bbAtOldPos = { x: pos.x, y: pos.y }; +module.exports = exprs; - if (newPos) { - pos.x = newPos.x; - pos.y = newPos.y; - } - } +/***/ }), +/* 51 */ +/***/ (function(module, exports, __webpack_require__) { - this.emit('dirty'); // let the renderer know we've manually dirtied rendered dim calcs +"use strict"; - nodes.dirtyCompoundBoundsCache().updateCompoundBounds(); - var bb = this.boundingBox({ useCache: false }); +var util = __webpack_require__(1); - // restore the original position, per node - for (var _i = 0; _i < nodes.length; _i++) { - var _n = nodes[_i]; - var _p2 = _n._private; - var _pos = _n._private.position; - var old = _p2.bbAtOldPos; +// tokens in the query language +var tokens = { + metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]', // chars we need to escape in let names, etc + comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=', // binary comparison op (used in data selectors) + boolOp: '\\?|\\!|\\^', // boolean (unary) operators (used in data selectors) + string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'", // string literals (used in data selectors) -- doublequotes | singlequotes + number: util.regex.number, // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123 + meta: 'degree|indegree|outdegree', // allowed metadata fields (i.e. allowed functions to use from Collection) + separator: '\\s*,\\s*', // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass + descendant: '\\s+', + child: '\\s+>\\s+', + subject: '\\$', + group: 'node|edge|\\*', + directedEdge: '\\s+->\\s+', + undirectedEdge: '\\s+<->\\s+' +}; +tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name +tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number +tokens.className = tokens.variable; // a class name (follows variable conventions) +tokens.id = tokens.variable; // an element id (follows variable conventions) - _pos.x = old.x; - _pos.y = old.y; - } +(function () { + var ops = void 0, + op = void 0, + i = void 0; - nodes.dirtyCompoundBoundsCache(); + // add @ variants to comparatorOp + ops = tokens.comparatorOp.split('|'); + for (i = 0; i < ops.length; i++) { + op = ops[i]; + tokens.comparatorOp += '|@' + op; + } - this.emit('dirty'); // let the renderer know we've manually dirtied rendered dim calcs + // add ! variants to comparatorOp + ops = tokens.comparatorOp.split('|'); + for (i = 0; i < ops.length; i++) { + op = ops[i]; - return bb; -}; + if (op.indexOf('!') >= 0) { + continue; + } // skip ops that explicitly contain ! + if (op === '=') { + continue; + } // skip = b/c != is explicitly defined -fn.boundingbox = fn.boundingBox; -fn.renderedBoundingbox = fn.renderedBoundingBox; + tokens.comparatorOp += '|\\!' + op; + } +})(); -module.exports = elesfn; +module.exports = tokens; /***/ }), -/* 43 */ +/* 52 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var ifEdge = function ifEdge(self, then) { - if (self.isEdge()) { - return then(self.renderer()); - } -}; - -module.exports = { - controlPoints: function controlPoints() { - var _this = this; +var _require = __webpack_require__(15), + stateSelectorMatches = _require.stateSelectorMatches; - return ifEdge(this, function (renderer) { - return renderer.getControlPoints(_this); - }); - }, - segmentPoints: function segmentPoints() { - var _this2 = this; +var is = __webpack_require__(0); - return ifEdge(this, function (renderer) { - return renderer.getSegmentPoints(_this2); - }); - }, - sourceEndpoint: function sourceEndpoint() { - var _this3 = this; +// generic checking for data/metadata +var operandsMatch = function operandsMatch(query, params) { + var allDataMatches = true; + for (var k = 0; k < query[params.name].length; k++) { + var data = query[params.name][k]; + var operator = data.operator; + var value = data.value; + var field = data.field; + var _matches = void 0; + var fieldVal = params.fieldValue(field); - return ifEdge(this, function (renderer) { - return renderer.getSourceEndpoint(_this3); - }); - }, - targetEndpoint: function targetEndpoint() { - var _this4 = this; + if (operator != null && value != null) { + var fieldStr = !is.string(fieldVal) && !is.number(fieldVal) ? '' : '' + fieldVal; + var valStr = '' + value; - return ifEdge(this, function (renderer) { - return renderer.getTargetEndpoint(_this4); - }); - }, - midpoint: function midpoint() { - var _this5 = this; + var caseInsensitive = false; + if (operator.indexOf('@') >= 0) { + fieldStr = fieldStr.toLowerCase(); + valStr = valStr.toLowerCase(); - return ifEdge(this, function (renderer) { - return renderer.getEdgeMidpoint(_this5); - }); - } -}; + operator = operator.replace('@', ''); + caseInsensitive = true; + } -/***/ }), -/* 44 */ -/***/ (function(module, exports, __webpack_require__) { + var notExpr = false; + if (operator.indexOf('!') >= 0) { + operator = operator.replace('!', ''); + notExpr = true; + } -"use strict"; + // if we're doing a case insensitive comparison, then we're using a STRING comparison + // even if we're comparing numbers + if (caseInsensitive) { + value = valStr.toLowerCase(); + fieldVal = fieldStr.toLowerCase(); + } + var isIneqCmp = false; -var util = __webpack_require__(1); -var position = __webpack_require__(45); -var bounds = __webpack_require__(42); -var widthHeight = __webpack_require__(46); -var edgePoints = __webpack_require__(43); + switch (operator) { + case '*=': + _matches = fieldStr.indexOf(valStr) >= 0; + break; + case '$=': + _matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0; + break; + case '^=': + _matches = fieldStr.indexOf(valStr) === 0; + break; + case '=': + _matches = fieldVal === value; + break; + case '>': + isIneqCmp = true; + _matches = fieldVal > value; + break; + case '>=': + isIneqCmp = true; + _matches = fieldVal >= value; + break; + case '<': + isIneqCmp = true; + _matches = fieldVal < value; + break; + case '<=': + isIneqCmp = true; + _matches = fieldVal <= value; + break; + default: + _matches = false; + break; + } -module.exports = util.assign({}, position, bounds, widthHeight, edgePoints); + // apply the not op, but null vals for inequalities should always stay non-matching + if (notExpr && (fieldVal != null || !isIneqCmp)) { + _matches = !_matches; + } + } else if (operator != null) { + switch (operator) { + case '?': + _matches = fieldVal ? true : false; + break; + case '!': + _matches = fieldVal ? false : true; + break; + case '^': + _matches = fieldVal === undefined; + break; + } + } else { + _matches = fieldVal !== undefined; + } -/***/ }), -/* 45 */ -/***/ (function(module, exports, __webpack_require__) { + if (!_matches) { + allDataMatches = false; + break; + } + } // for -"use strict"; + return allDataMatches; +}; // operandsMatch +// check parent/child relations +var confirmRelations = function confirmRelations(query, isNecessary, eles) { + if (query != null) { + var _matches2 = false; -var define = __webpack_require__(3); -var is = __webpack_require__(0); -var math = __webpack_require__(2); -var fn = void 0, - elesfn = void 0; - -var beforePositionSet = function beforePositionSet(eles, newPos) { - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; + if (!isNecessary) { + return false; + } - if (ele.isParent() && !ele.locked()) { - var oldPos = ele._private.position; - var delta = { - x: newPos.x - oldPos.x, - y: newPos.y - oldPos.y - }; + eles = eles(); // save cycles if query == null - eles.children().shift(delta); + // query must match for at least one element (may be recursive) + for (var i = 0; i < eles.length; i++) { + if (queryMatches(query, eles[i])) { + _matches2 = true; + break; + } } + + return _matches2; + } else { + return true; } }; -fn = elesfn = { +var queryMatches = function queryMatches(query, ele) { + // make single group-only selectors really cheap to check since they're the most common ones + if (query.groupOnly) { + return query.group === '*' || query.group === ele.group(); + } - position: define.data({ - field: 'position', - bindingEvent: 'position', - allowBinding: true, - allowSetting: true, - settingEvent: 'position', - settingTriggersEvent: true, - triggerFnName: 'emitAndNotify', - allowGetting: true, - validKeys: ['x', 'y'], - beforeGet: function beforeGet(ele) { - ele.updateCompoundBounds(); - }, - beforeSet: beforePositionSet, - onSet: function onSet(eles) { - eles.dirtyCompoundBoundsCache(); - }, - canSet: function canSet(ele) { - return !ele.locked(); - } - }), + // check group + if (query.group != null && query.group != '*' && query.group != ele.group()) { + return false; + } - // position but no notification to renderer - silentPosition: define.data({ - field: 'position', - bindingEvent: 'position', - allowBinding: false, - allowSetting: true, - settingEvent: 'position', - settingTriggersEvent: false, - triggerFnName: 'trigger', - allowGetting: false, - validKeys: ['x', 'y'], - beforeSet: beforePositionSet, - onSet: function onSet(eles) { - eles.dirtyCompoundBoundsCache(); - }, - canSet: function canSet(ele) { - return !ele.locked(); - } - }), + var cy = ele.cy(); + var k = void 0; - positions: function positions(pos, silent) { - if (is.plainObject(pos)) { - if (silent) { - this.silentPosition(pos); - } else { - this.position(pos); - } - } else if (is.fn(pos)) { - var _fn = pos; - var cy = this.cy(); + // check colon selectors + var allColonSelectorsMatch = true; + for (k = 0; k < query.colonSelectors.length; k++) { + var sel = query.colonSelectors[k]; - cy.startBatch(); + allColonSelectorsMatch = stateSelectorMatches(sel, ele); - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - var _pos = void 0; + if (!allColonSelectorsMatch) break; + } + if (!allColonSelectorsMatch) return false; - if (_pos = _fn(ele, i)) { - if (silent) { - ele.silentPosition(_pos); - } else { - ele.position(_pos); - } - } - } + // check id + var allIdsMatch = true; + for (k = 0; k < query.ids.length; k++) { + var id = query.ids[k]; + var actualId = ele.id(); - cy.endBatch(); - } + allIdsMatch = allIdsMatch && id == actualId; - return this; // chaining - }, + if (!allIdsMatch) break; + } + if (!allIdsMatch) return false; - silentPositions: function silentPositions(pos) { - return this.positions(pos, true); - }, + // check classes + var allClassesMatch = true; + for (k = 0; k < query.classes.length; k++) { + var cls = query.classes[k]; - shift: function shift(dim, val) { - var delta = void 0; + allClassesMatch = allClassesMatch && ele.hasClass(cls); - if (is.plainObject(dim)) { - delta = dim; - } else if (is.string(dim) && is.number(val)) { - delta = { x: 0, y: 0 }; + if (!allClassesMatch) break; + } + if (!allClassesMatch) return false; - delta[dim] = val; + // check data matches + var allDataMatches = operandsMatch(query, { + name: 'data', + fieldValue: function fieldValue(field) { + return ele.data(field); } + }); - if (delta != null) { - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - var pos = ele.position(); + if (!allDataMatches) { + return false; + } - ele.position({ - x: pos.x + delta.x, - y: pos.y + delta.y - }); - } + // check metadata matches + var allMetaMatches = operandsMatch(query, { + name: 'meta', + fieldValue: function fieldValue(field) { + return ele[field](); } + }); - return this; - }, - - // get/set the rendered (i.e. on screen) positon of the element - renderedPosition: function renderedPosition(dim, val) { - var ele = this[0]; - var cy = this.cy(); - var zoom = cy.zoom(); - var pan = cy.pan(); - var rpos = is.plainObject(dim) ? dim : undefined; - var setting = rpos !== undefined || val !== undefined && is.string(dim); - - if (ele && ele.isNode()) { - // must have an element and must be a node to return position - if (setting) { - for (var i = 0; i < this.length; i++) { - var _ele = this[i]; + if (!allMetaMatches) { + return false; + } - if (val !== undefined) { - // set one dimension - _ele.position(dim, (val - pan[dim]) / zoom); - } else if (rpos !== undefined) { - // set whole position - _ele.position(math.renderedToModelPosition(rpos, zoom, pan)); - } - } - } else { - // getting - var pos = ele.position(); - rpos = math.modelToRenderedPosition(pos, zoom, pan); + // check collection + if (query.collection != null) { + var matchesAny = query.collection.hasElementWithId(ele.id()); - if (dim === undefined) { - // then return the whole rendered position - return rpos; - } else { - // then return the specified dimension - return rpos[dim]; - } - } - } else if (!setting) { - return undefined; // for empty collection case + if (!matchesAny) { + return false; } + } - return this; // chaining - }, - - // get/set the position relative to the parent - relativePosition: function relativePosition(dim, val) { - var ele = this[0]; - var cy = this.cy(); - var ppos = is.plainObject(dim) ? dim : undefined; - var setting = ppos !== undefined || val !== undefined && is.string(dim); - var hasCompoundNodes = cy.hasCompoundNodes(); + // check filter function + if (query.filter != null && ele.collection().some(query.filter)) { + return false; + } - if (ele && ele.isNode()) { - // must have an element and must be a node to return position - if (setting) { - for (var i = 0; i < this.length; i++) { - var _ele2 = this[i]; - var parent = hasCompoundNodes ? _ele2.parent() : null; - var hasParent = parent && parent.length > 0; - var relativeToParent = hasParent; + var isCompound = cy.hasCompoundNodes(); + var getSource = function getSource() { + return ele.source(); + }; + var getTarget = function getTarget() { + return ele.target(); + }; - if (hasParent) { - parent = parent[0]; - } + if (!confirmRelations(query.parent, isCompound, function () { + return ele.parent(); + })) { + return false; + } - var origin = relativeToParent ? parent.position() : { x: 0, y: 0 }; + if (!confirmRelations(query.ancestor, isCompound, function () { + return ele.parents(); + })) { + return false; + } - if (val !== undefined) { - // set one dimension - _ele2.position(dim, val + origin[dim]); - } else if (ppos !== undefined) { - // set whole position - _ele2.position({ - x: ppos.x + origin.x, - y: ppos.y + origin.y - }); - } - } - } else { - // getting - var pos = ele.position(); - var _parent = hasCompoundNodes ? ele.parent() : null; - var _hasParent = _parent && _parent.length > 0; - var _relativeToParent = _hasParent; + if (!confirmRelations(query.child, isCompound, function () { + return ele.children(); + })) { + return false; + } - if (_hasParent) { - _parent = _parent[0]; - } + if (!confirmRelations(query.descendant, isCompound, function () { + return ele.descendants(); + })) { + return false; + } - var _origin = _relativeToParent ? _parent.position() : { x: 0, y: 0 }; + if (!confirmRelations(query.source, true, getSource)) { + return false; + } - ppos = { - x: pos.x - _origin.x, - y: pos.y - _origin.y - }; + if (!confirmRelations(query.target, true, getTarget)) { + return false; + } - if (dim === undefined) { - // then return the whole rendered position - return ppos; - } else { - // then return the specified dimension - return ppos[dim]; - } - } - } else if (!setting) { - return undefined; // for empty collection case + if (query.connectedNodes) { + var q0 = query.connectedNodes[0]; + var q1 = query.connectedNodes[1]; + + if (confirmRelations(q0, true, getSource) && confirmRelations(q1, true, getTarget)) { + // match + } else if (confirmRelations(q0, true, getTarget) && confirmRelations(q1, true, getSource)) { + // match + } else { + return false; } + } - return this; // chaining + // we've reached the end, so we've matched everything for this query + return true; +}; // queryMatches + +// filter an existing collection +var filter = function filter(collection) { + var self = this; + var cy = collection.cy(); + + // don't bother trying if it's invalid + if (self.invalid()) { + return cy.collection(); } -}; -// aliases -fn.modelPosition = fn.point = fn.position; -fn.modelPositions = fn.points = fn.positions; -fn.renderedPoint = fn.renderedPosition; -fn.relativePoint = fn.relativePosition; + // for 1 id #foo queries, just get the element + if (self.length === 1 && self[0].length === 1 && self[0].ids.length === 1) { + return collection.getElementById(self[0].ids[0]).collection(); + } -module.exports = elesfn; + var selectorFunction = function selectorFunction(element) { + for (var j = 0; j < self.length; j++) { + var query = self[j]; -/***/ }), -/* 46 */ -/***/ (function(module, exports, __webpack_require__) { + if (queryMatches(query, element)) { + return true; + } + } -"use strict"; + return false; + }; + if (self.text() == null) { + selectorFunction = function selectorFunction() { + return true; + }; + } -var util = __webpack_require__(1); -var fn = void 0, - elesfn = void 0; + var filteredCollection = collection.filter(selectorFunction); -fn = elesfn = {}; + return filteredCollection; +}; // filter -var defineDimFns = function defineDimFns(opts) { - opts.uppercaseName = util.capitalize(opts.name); - opts.autoName = 'auto' + opts.uppercaseName; - opts.labelName = 'label' + opts.uppercaseName; - opts.outerName = 'outer' + opts.uppercaseName; - opts.uppercaseOuterName = util.capitalize(opts.outerName); +// does selector match a single element? +var matches = function matches(ele) { + var self = this; - fn[opts.name] = function dimImpl() { - var ele = this[0]; - var _p = ele._private; - var cy = _p.cy; - var styleEnabled = cy._private.styleEnabled; + // don't bother trying if it's invalid + if (self.invalid()) { + return false; + } - if (ele) { - if (styleEnabled) { - if (ele.isParent()) { - ele.updateCompoundBounds(); + for (var j = 0; j < self.length; j++) { + var query = self[j]; - return _p[opts.autoName] || 0; - } + if (queryMatches(query, ele)) { + return true; + } + } - var d = ele.pstyle(opts.name); + return false; +}; // filter - switch (d.strValue) { - case 'label': - ele.recalculateRenderedStyle(); +module.exports = { matches: matches, filter: filter }; - return _p.rstyle[opts.labelName] || 0; +/***/ }), +/* 53 */ +/***/ (function(module, exports, __webpack_require__) { - default: - return d.pfValue; - } - } else { - return 1; - } - } - }; +"use strict"; - fn['outer' + opts.uppercaseName] = function outerDimImpl() { - var ele = this[0]; - var _p = ele._private; - var cy = _p.cy; - var styleEnabled = cy._private.styleEnabled; - if (ele) { - if (styleEnabled) { - var dim = ele[opts.name](); - var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side - var padding = 2 * ele.padding(); +var Set = __webpack_require__(8); - return dim + border + padding; - } else { - return 1; - } - } - }; +var elesfn = { + parent: function parent(selector) { + var parents = []; - fn['rendered' + opts.uppercaseName] = function renderedDimImpl() { - var ele = this[0]; + // optimisation for single ele call + if (this.length === 1) { + var parent = this[0]._private.parent; - if (ele) { - var d = ele[opts.name](); - return d * this.cy().zoom(); + if (parent) { + return parent; + } } - }; - fn['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() { - var ele = this[0]; + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + var _parent = ele._private.parent; - if (ele) { - var od = ele[opts.outerName](); - return od * this.cy().zoom(); + if (_parent) { + parents.push(_parent); + } } - }; -}; -defineDimFns({ - name: 'width' -}); + return this.spawn(parents, { unique: true }).filter(selector); + }, -defineDimFns({ - name: 'height' -}); + parents: function parents(selector) { + var parents = []; -elesfn.padding = function () { - var ele = this[0]; - var _p = ele._private; - if (ele.isParent()) { - ele.updateCompoundBounds(); + var eles = this.parent(); + while (eles.nonempty()) { + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + parents.push(ele); + } - if (_p.autoPadding !== undefined) { - return _p.autoPadding; - } else { - return ele.pstyle('padding').pfValue; + eles = eles.parent(); } - } else { - return ele.pstyle('padding').pfValue; - } -}; -module.exports = elesfn; + return this.spawn(parents, { unique: true }).filter(selector); + }, -/***/ }), -/* 47 */ -/***/ (function(module, exports, __webpack_require__) { + commonAncestors: function commonAncestors(selector) { + var ancestors = void 0; -"use strict"; + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + var parents = ele.parents(); + ancestors = ancestors || parents; -var Emitter = __webpack_require__(10); -var define = __webpack_require__(3); -var is = __webpack_require__(0); -var util = __webpack_require__(1); -var Selector = __webpack_require__(6); + ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set + } -var emitterOptions = { - qualifierCompare: function qualifierCompare(selector1, selector2) { - if (selector1 == null || selector2 == null) { - return selector1 == null && selector2 == null; - } else { - return selector1.sameText(selector2); - } + return ancestors.filter(selector); }, - eventMatches: function eventMatches(ele, listener, eventObj) { - var selector = listener.qualifier; - - if (selector != null) { - return ele !== eventObj.target && is.element(eventObj.target) && selector.matches(eventObj.target); - } - return true; - }, - eventFields: function eventFields(ele) { - return { - cy: ele.cy(), - target: ele - }; - }, - callbackContext: function callbackContext(ele, listener, eventObj) { - return listener.qualifier != null ? eventObj.target : ele; - }, - beforeEmit: function beforeEmit(context, listener /*, eventObj*/) { - if (listener.conf && listener.conf.once) { - listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback); - } + orphans: function orphans(selector) { + return this.stdFilter(function (ele) { + return ele.isOrphan(); + }).filter(selector); }, - bubble: function bubble() { - return true; + + nonorphans: function nonorphans(selector) { + return this.stdFilter(function (ele) { + return ele.isChild(); + }).filter(selector); }, - parent: function parent(ele) { - return ele.isChild() ? ele.parent() : ele.cy(); - } -}; -var argSelector = function argSelector(arg) { - if (is.string(arg)) { - return new Selector(arg); - } else { - return arg; - } -}; + children: function children(selector) { + var children = []; -var elesfn = { - createEmitter: function createEmitter() { for (var i = 0; i < this.length; i++) { var ele = this[i]; - var _p = ele._private; - - if (!_p.emitter) { - _p.emitter = new Emitter(util.assign({ - context: ele - }, emitterOptions)); - } + children = children.concat(ele._private.children); } - return this; + return this.spawn(children, { unique: true }).filter(selector); }, - emitter: function emitter() { - return this._private.emitter; + siblings: function siblings(selector) { + return this.parent().children().not(this).filter(selector); }, - on: function on(events, selector, callback) { - for (var i = 0; i < this.length; i++) { - var ele = this[i]; + isParent: function isParent() { + var ele = this[0]; - ele.emitter().on(events, argSelector(selector), callback); + if (ele) { + return ele.isNode() && ele._private.children.length !== 0; } - - return this; }, - removeListener: function removeListener(events, selector, callback) { - for (var i = 0; i < this.length; i++) { - var ele = this[i]; + isChildless: function isChildless() { + var ele = this[0]; - ele.emitter().removeListener(events, argSelector(selector), callback); + if (ele) { + return ele.isNode() && ele._private.children.length === 0; } - - return this; }, - one: function one(events, selector, callback) { - for (var i = 0; i < this.length; i++) { - var ele = this[i]; + isChild: function isChild() { + var ele = this[0]; - ele.emitter().one(events, argSelector(selector), callback); + if (ele) { + return ele.isNode() && ele._private.parent != null; } - - return this; }, - once: function once(events, selector, callback) { - for (var i = 0; i < this.length; i++) { - var ele = this[i]; + isOrphan: function isOrphan() { + var ele = this[0]; - ele.emitter().on(events, argSelector(selector), callback, { - once: true, - onceCollection: this - }); + if (ele) { + return ele.isNode() && ele._private.parent == null; } }, - emit: function emit(events, extraParams) { - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - - ele.emitter().emit(events, extraParams); - } + descendants: function descendants(selector) { + var elements = []; - return this; - }, + function add(eles) { + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - emitAndNotify: function emitAndNotify(event, extraParams) { - // for internal use only - if (this.length === 0) { - return; - } // empty collections don't need to notify anything + elements.push(ele); - // notify renderer - this.cy().notify({ - type: event, - eles: this - }); + if (ele.children().nonempty()) { + add(ele.children()); + } + } + } - this.emit(event, extraParams); + add(this.children()); - return this; + return this.spawn(elements, { unique: true }).filter(selector); } }; -define.eventAliasesOn(elesfn); +function forEachCompound(eles, fn, includeSelf, recursiveStep) { + var q = []; + var did = new Set(); + var cy = eles.cy(); + var hasCompounds = cy.hasCompoundNodes(); -module.exports = elesfn; + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; -/***/ }), -/* 48 */ -/***/ (function(module, exports, __webpack_require__) { + if (includeSelf) { + q.push(ele); + } else if (hasCompounds) { + recursiveStep(q, did, ele); + } + } -"use strict"; + while (q.length > 0) { + var _ele = q.shift(); + fn(_ele); -var is = __webpack_require__(0); -var Selector = __webpack_require__(6); + did.add(_ele.id()); -var elesfn = { - nodes: function nodes(selector) { - return this.filter(function (ele) { - return ele.isNode(); - }).filter(selector); - }, + if (hasCompounds) { + recursiveStep(q, did, _ele); + } + } - edges: function edges(selector) { - return this.filter(function (ele) { - return ele.isEdge(); - }).filter(selector); - }, + return eles; +} - filter: function filter(_filter, thisArg) { - if (_filter === undefined) { - // check this first b/c it's the most common/performant case - return this; - } else if (is.string(_filter) || is.elementOrCollection(_filter)) { - return new Selector(_filter).filter(this); - } else if (is.fn(_filter)) { - var filterEles = this.spawn(); - var eles = this; +function addChildren(q, did, ele) { + if (ele.isParent()) { + var children = ele._private.children; - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles); + for (var i = 0; i < children.length; i++) { + var child = children[i]; - if (include) { - filterEles.merge(ele); - } + if (!did.has(child.id())) { + q.push(child); } - - return filterEles; } + } +} - return this.spawn(); // if not handled by above, give 'em an empty collection - }, +// very efficient version of eles.add( eles.descendants() ).forEach() +// for internal use +elesfn.forEachDown = function (fn) { + var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - not: function not(toRemove) { - if (!toRemove) { - return this; - } else { + return forEachCompound(this, fn, includeSelf, addChildren); +}; - if (is.string(toRemove)) { - toRemove = this.filter(toRemove); - } +function addParent(q, did, ele) { + if (ele.isChild()) { + var parent = ele._private.parent; - var elements = []; - var rMap = toRemove._private.map; + if (!did.has(parent.id())) { + q.push(parent); + } + } +} - for (var i = 0; i < this.length; i++) { - var element = this[i]; +elesfn.forEachUp = function (fn) { + var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var remove = rMap.has(element.id()); - if (!remove) { - elements.push(element); - } - } + return forEachCompound(this, fn, includeSelf, addParent); +}; - return this.spawn(elements); - } - }, +function addParentAndChildren(q, did, ele) { + addParent(q, did, ele); + addChildren(q, did, ele); +} - absoluteComplement: function absoluteComplement() { - var cy = this.cy(); +elesfn.forEachUpAndDown = function (fn) { + var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - return cy.mutableElements().not(this); - }, + return forEachCompound(this, fn, includeSelf, addParentAndChildren); +}; - intersect: function intersect(other) { - // if a selector is specified, then filter by it instead - if (is.string(other)) { - var selector = other; - return this.filter(selector); - } +// aliases +elesfn.ancestors = elesfn.parents; - var elements = []; - var col1 = this; - var col2 = other; - var col1Smaller = this.length < other.length; - var map2 = col1Smaller ? col2._private.map : col1._private.map; - var col = col1Smaller ? col1 : col2; +module.exports = elesfn; - for (var i = 0; i < col.length; i++) { - var id = col[i]._private.data.id; - var entry = map2.get(id); +/***/ }), +/* 54 */ +/***/ (function(module, exports, __webpack_require__) { - if (entry) { - elements.push(entry.ele); - } - } +"use strict"; - return this.spawn(elements); - }, - xor: function xor(other) { - var cy = this._private.cy; +var define = __webpack_require__(4); +var fn = void 0, + elesfn = void 0; - if (is.string(other)) { - other = cy.$(other); - } +fn = elesfn = { - var elements = []; - var col1 = this; - var col2 = other; + data: define.data({ + field: 'data', + bindingEvent: 'data', + allowBinding: true, + allowSetting: true, + settingEvent: 'data', + settingTriggersEvent: true, + triggerFnName: 'trigger', + allowGetting: true, + immutableKeys: { + 'id': true, + 'source': true, + 'target': true, + 'parent': true + }, + updateStyle: true + }), - var add = function add(col, other) { - for (var i = 0; i < col.length; i++) { - var ele = col[i]; - var id = ele._private.data.id; - var inOther = other.hasElementWithId(id); + removeData: define.removeData({ + field: 'data', + event: 'data', + triggerFnName: 'trigger', + triggerEvent: true, + immutableKeys: { + 'id': true, + 'source': true, + 'target': true, + 'parent': true + }, + updateStyle: true + }), - if (!inOther) { - elements.push(ele); - } - } - }; + scratch: define.data({ + field: 'scratch', + bindingEvent: 'scratch', + allowBinding: true, + allowSetting: true, + settingEvent: 'scratch', + settingTriggersEvent: true, + triggerFnName: 'trigger', + allowGetting: true, + updateStyle: true + }), - add(col1, col2); - add(col2, col1); + removeScratch: define.removeData({ + field: 'scratch', + event: 'scratch', + triggerFnName: 'trigger', + triggerEvent: true, + updateStyle: true + }), - return this.spawn(elements); - }, + rscratch: define.data({ + field: 'rscratch', + allowBinding: false, + allowSetting: true, + settingTriggersEvent: false, + allowGetting: true + }), - diff: function diff(other) { - var cy = this._private.cy; + removeRscratch: define.removeData({ + field: 'rscratch', + triggerEvent: false + }), - if (is.string(other)) { - other = cy.$(other); + id: function id() { + var ele = this[0]; + + if (ele) { + return ele._private.data.id; } + } - var left = []; - var right = []; - var both = []; - var col1 = this; - var col2 = other; +}; - var add = function add(col, other, retEles) { +// aliases +fn.attr = fn.data; +fn.removeAttr = fn.removeData; - for (var i = 0; i < col.length; i++) { - var ele = col[i]; - var id = ele._private.data.id; - var inOther = other.hasElementWithId(id); +module.exports = elesfn; - if (inOther) { - both.push(ele); - } else { - retEles.push(ele); - } - } - }; +/***/ }), +/* 55 */ +/***/ (function(module, exports, __webpack_require__) { - add(col1, col2, left); - add(col2, col1, right); +"use strict"; - return { - left: this.spawn(left, { unique: true }), - right: this.spawn(right, { unique: true }), - both: this.spawn(both, { unique: true }) - }; - }, - add: function add(toAdd) { - var cy = this._private.cy; +var util = __webpack_require__(1); - if (!toAdd) { - return this; - } +var elesfn = {}; - if (is.string(toAdd)) { - var selector = toAdd; - toAdd = cy.mutableElements().filter(selector); - } +function defineDegreeFunction(callback) { + return function (includeLoops) { + var self = this; - var elements = []; + if (includeLoops === undefined) { + includeLoops = true; + } - for (var i = 0; i < this.length; i++) { - elements.push(this[i]); + if (self.length === 0) { + return; } - var map = this._private.map; + if (self.isNode() && !self.removed()) { + var degree = 0; + var node = self[0]; + var connectedEdges = node._private.edges; - for (var _i = 0; _i < toAdd.length; _i++) { + for (var i = 0; i < connectedEdges.length; i++) { + var edge = connectedEdges[i]; - var add = !map.has(toAdd[_i].id()); - if (add) { - elements.push(toAdd[_i]); - } - } + if (!includeLoops && edge.isLoop()) { + continue; + } - return this.spawn(elements); - }, + degree += callback(node, edge); + } - // in place merge on calling collection - merge: function merge(toAdd) { - var _p = this._private; - var cy = _p.cy; + return degree; + } else { + return; + } + }; +} - if (!toAdd) { - return this; +util.extend(elesfn, { + degree: defineDegreeFunction(function (node, edge) { + if (edge.source().same(edge.target())) { + return 2; + } else { + return 1; } + }), - if (toAdd && is.string(toAdd)) { - var selector = toAdd; - toAdd = cy.mutableElements().filter(selector); + indegree: defineDegreeFunction(function (node, edge) { + if (edge.target().same(node)) { + return 1; + } else { + return 0; } + }), - var map = _p.map; + outdegree: defineDegreeFunction(function (node, edge) { + if (edge.source().same(node)) { + return 1; + } else { + return 0; + } + }) +}); - for (var i = 0; i < toAdd.length; i++) { - var toAddEle = toAdd[i]; - var id = toAddEle._private.data.id; - var add = !map.has(id); +function defineDegreeBoundsFunction(degreeFn, callback) { + return function (includeLoops) { + var ret = void 0; + var nodes = this.nodes(); - if (add) { - var index = this.length++; + for (var i = 0; i < nodes.length; i++) { + var ele = nodes[i]; + var degree = ele[degreeFn](includeLoops); + if (degree !== undefined && (ret === undefined || callback(degree, ret))) { + ret = degree; + } + } - this[index] = toAddEle; + return ret; + }; +} - map.set(id, { ele: toAddEle, index: index }); - } else { - // replace - var _index = map.get(id).index; +util.extend(elesfn, { + minDegree: defineDegreeBoundsFunction('degree', function (degree, min) { + return degree < min; + }), - this[_index] = toAddEle; - map.set(id, { ele: toAddEle, index: _index }); - } - } + maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) { + return degree > max; + }), - return this; // chaining - }, + minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) { + return degree < min; + }), - // remove single ele in place in calling collection - unmergeOne: function unmergeOne(ele) { - ele = ele[0]; + maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) { + return degree > max; + }), - var _p = this._private; - var id = ele._private.data.id; - var map = _p.map; - var entry = map.get(id); + minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) { + return degree < min; + }), - if (!entry) { - return this; // no need to remove - } + maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) { + return degree > max; + }) +}); - var i = entry.index; +util.extend(elesfn, { + totalDegree: function totalDegree(includeLoops) { + var total = 0; + var nodes = this.nodes(); - // remove ele - this[i] = undefined; - map.delete(id); + for (var i = 0; i < nodes.length; i++) { + total += nodes[i].degree(includeLoops); + } - var unmergedLastEle = i === this.length - 1; + return total; + } +}); - // replace empty spot with last ele in collection - if (this.length > 1 && !unmergedLastEle) { - var lastEleI = this.length - 1; - var lastEle = this[lastEleI]; - var lastEleId = lastEle._private.data.id; +module.exports = elesfn; - this[lastEleI] = undefined; - this[i] = lastEle; - map.set(lastEleId, { ele: lastEle, index: i }); - } +/***/ }), +/* 56 */ +/***/ (function(module, exports, __webpack_require__) { - // the collection is now 1 ele smaller - this.length--; +"use strict"; - return this; - }, - // remove eles in place on calling collection - unmerge: function unmerge(toRemove) { - var cy = this._private.cy; +var util = __webpack_require__(1); +var position = __webpack_require__(57); +var bounds = __webpack_require__(58); +var widthHeight = __webpack_require__(59); +var edgePoints = __webpack_require__(60); - if (!toRemove) { - return this; - } +module.exports = util.assign({}, position, bounds, widthHeight, edgePoints); - if (toRemove && is.string(toRemove)) { - var selector = toRemove; - toRemove = cy.mutableElements().filter(selector); - } +/***/ }), +/* 57 */ +/***/ (function(module, exports, __webpack_require__) { - for (var i = 0; i < toRemove.length; i++) { - this.unmergeOne(toRemove[i]); - } +"use strict"; - return this; // chaining - }, - map: function map(mapFn, thisArg) { - var arr = []; - var eles = this; +var define = __webpack_require__(4); +var is = __webpack_require__(0); +var math = __webpack_require__(2); +var fn = void 0, + elesfn = void 0; - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles); +var beforePositionSet = function beforePositionSet(eles, newPos) { + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - arr.push(ret); + if (ele.isParent() && !ele.locked()) { + var oldPos = ele._private.position; + var delta = { + x: newPos.x - oldPos.x, + y: newPos.y - oldPos.y + }; + + eles.children().shift(delta); } + } +}; - return arr; - }, +fn = elesfn = { - reduce: function reduce(fn, initialValue) { - var val = initialValue; - var eles = this; + position: define.data({ + field: 'position', + bindingEvent: 'position', + allowBinding: true, + allowSetting: true, + settingEvent: 'position', + settingTriggersEvent: true, + triggerFnName: 'emitAndNotify', + allowGetting: true, + validKeys: ['x', 'y'], + beforeGet: function beforeGet(ele) { + ele.updateCompoundBounds(); + }, + beforeSet: beforePositionSet, + onSet: function onSet(eles) { + eles.dirtyCompoundBoundsCache(); + }, + canSet: function canSet(ele) { + return !ele.locked(); + } + }), - for (var i = 0; i < eles.length; i++) { - val = fn(val, eles[i], i, eles); + // position but no notification to renderer + silentPosition: define.data({ + field: 'position', + bindingEvent: 'position', + allowBinding: false, + allowSetting: true, + settingEvent: 'position', + settingTriggersEvent: false, + triggerFnName: 'trigger', + allowGetting: false, + validKeys: ['x', 'y'], + beforeSet: beforePositionSet, + onSet: function onSet(eles) { + eles.dirtyCompoundBoundsCache(); + }, + canSet: function canSet(ele) { + return !ele.locked(); } + }), - return val; - }, + positions: function positions(pos, silent) { + if (is.plainObject(pos)) { + if (silent) { + this.silentPosition(pos); + } else { + this.position(pos); + } + } else if (is.fn(pos)) { + var _fn = pos; + var cy = this.cy(); - max: function max(valFn, thisArg) { - var max = -Infinity; - var maxEle = void 0; - var eles = this; + cy.startBatch(); - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles); + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + var _pos = void 0; - if (val > max) { - max = val; - maxEle = ele; + if (_pos = _fn(ele, i)) { + if (silent) { + ele.silentPosition(_pos); + } else { + ele.position(_pos); + } + } } + + cy.endBatch(); } - return { - value: max, - ele: maxEle - }; + return this; // chaining }, - min: function min(valFn, thisArg) { - var min = Infinity; - var minEle = void 0; - var eles = this; + silentPositions: function silentPositions(pos) { + return this.positions(pos, true); + }, - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles); + shift: function shift(dim, val) { + var delta = void 0; - if (val < min) { - min = val; - minEle = ele; - } - } + if (is.plainObject(dim)) { + delta = dim; + } else if (is.string(dim) && is.number(val)) { + delta = { x: 0, y: 0 }; - return { - value: min, - ele: minEle - }; - } -}; + delta[dim] = val; + } -// aliases -var fn = elesfn; -fn['u'] = fn['|'] = fn['+'] = fn.union = fn.or = fn.add; -fn['\\'] = fn['!'] = fn['-'] = fn.difference = fn.relativeComplement = fn.subtract = fn.not; -fn['n'] = fn['&'] = fn['.'] = fn.and = fn.intersection = fn.intersect; -fn['^'] = fn['(+)'] = fn['(-)'] = fn.symmetricDifference = fn.symdiff = fn.xor; -fn.fnFilter = fn.filterFn = fn.stdFilter = fn.filter; -fn.complement = fn.abscomp = fn.absoluteComplement; + if (delta != null) { + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + var pos = ele.position(); -module.exports = elesfn; + ele.position({ + x: pos.x + delta.x, + y: pos.y + delta.y + }); + } + } -/***/ }), -/* 49 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; + return this; + }, + // get/set the rendered (i.e. on screen) positon of the element + renderedPosition: function renderedPosition(dim, val) { + var ele = this[0]; + var cy = this.cy(); + var zoom = cy.zoom(); + var pan = cy.pan(); + var rpos = is.plainObject(dim) ? dim : undefined; + var setting = rpos !== undefined || val !== undefined && is.string(dim); -var elesfn = { - isNode: function isNode() { - return this.group() === 'nodes'; - }, + if (ele && ele.isNode()) { + // must have an element and must be a node to return position + if (setting) { + for (var i = 0; i < this.length; i++) { + var _ele = this[i]; - isEdge: function isEdge() { - return this.group() === 'edges'; - }, + if (val !== undefined) { + // set one dimension + _ele.position(dim, (val - pan[dim]) / zoom); + } else if (rpos !== undefined) { + // set whole position + _ele.position(math.renderedToModelPosition(rpos, zoom, pan)); + } + } + } else { + // getting + var pos = ele.position(); + rpos = math.modelToRenderedPosition(pos, zoom, pan); - isLoop: function isLoop() { - return this.isEdge() && this.source().id() === this.target().id(); - }, + if (dim === undefined) { + // then return the whole rendered position + return rpos; + } else { + // then return the specified dimension + return rpos[dim]; + } + } + } else if (!setting) { + return undefined; // for empty collection case + } - isSimple: function isSimple() { - return this.isEdge() && this.source().id() !== this.target().id(); + return this; // chaining }, - group: function group() { + // get/set the position relative to the parent + relativePosition: function relativePosition(dim, val) { var ele = this[0]; + var cy = this.cy(); + var ppos = is.plainObject(dim) ? dim : undefined; + var setting = ppos !== undefined || val !== undefined && is.string(dim); + var hasCompoundNodes = cy.hasCompoundNodes(); - if (ele) { - return ele._private.group; + if (ele && ele.isNode()) { + // must have an element and must be a node to return position + if (setting) { + for (var i = 0; i < this.length; i++) { + var _ele2 = this[i]; + var parent = hasCompoundNodes ? _ele2.parent() : null; + var hasParent = parent && parent.length > 0; + var relativeToParent = hasParent; + + if (hasParent) { + parent = parent[0]; + } + + var origin = relativeToParent ? parent.position() : { x: 0, y: 0 }; + + if (val !== undefined) { + // set one dimension + _ele2.position(dim, val + origin[dim]); + } else if (ppos !== undefined) { + // set whole position + _ele2.position({ + x: ppos.x + origin.x, + y: ppos.y + origin.y + }); + } + } + } else { + // getting + var pos = ele.position(); + var _parent = hasCompoundNodes ? ele.parent() : null; + var _hasParent = _parent && _parent.length > 0; + var _relativeToParent = _hasParent; + + if (_hasParent) { + _parent = _parent[0]; + } + + var _origin = _relativeToParent ? _parent.position() : { x: 0, y: 0 }; + + ppos = { + x: pos.x - _origin.x, + y: pos.y - _origin.y + }; + + if (dim === undefined) { + // then return the whole rendered position + return ppos; + } else { + // then return the specified dimension + return ppos[dim]; + } + } + } else if (!setting) { + return undefined; // for empty collection case } + + return this; // chaining } }; +// aliases +fn.modelPosition = fn.point = fn.position; +fn.modelPositions = fn.points = fn.positions; +fn.renderedPoint = fn.renderedPosition; +fn.relativePoint = fn.relativePosition; + module.exports = elesfn; /***/ }), -/* 50 */ +/* 58 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var is = __webpack_require__(0); -var zIndexSort = __webpack_require__(14); var util = __webpack_require__(1); +var math = __webpack_require__(2); +var fn = void 0, + elesfn = void 0; -var elesfn = { - forEach: function forEach(fn, thisArg) { - if (is.fn(fn)) { +fn = elesfn = {}; - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - var ret = thisArg ? fn.apply(thisArg, [ele, i, this]) : fn(ele, i, this); +elesfn.renderedBoundingBox = function (options) { + var bb = this.boundingBox(options); + var cy = this.cy(); + var zoom = cy.zoom(); + var pan = cy.pan(); - if (ret === false) { - break; - } // exit each early on return false - } - } + var x1 = bb.x1 * zoom + pan.x; + var x2 = bb.x2 * zoom + pan.x; + var y1 = bb.y1 * zoom + pan.y; + var y2 = bb.y2 * zoom + pan.y; + + return { + x1: x1, + x2: x2, + y1: y1, + y2: y2, + w: x2 - x1, + h: y2 - y1 + }; +}; + +elesfn.dirtyCompoundBoundsCache = function () { + var cy = this.cy(); + if (!cy.styleEnabled() || !cy.hasCompoundNodes()) { return this; - }, + } - toArray: function toArray() { - var array = []; + this.forEachUp(function (ele) { + ele._private.compoundBoundsClean = false; - for (var i = 0; i < this.length; i++) { - array.push(this[i]); + if (ele.isParent()) { + ele.emit('bounds'); } + }); - return array; - }, + return this; +}; - slice: function slice(start, end) { - var array = []; - var thisSize = this.length; +elesfn.updateCompoundBounds = function () { + var cy = this.cy(); - if (end == null) { - end = thisSize; - } + // save cycles for non compound graphs or when style disabled + if (!cy.styleEnabled() || !cy.hasCompoundNodes()) { + return this; + } - if (start == null) { - start = 0; - } + // save cycles when batching -- but bounds will be stale (or not exist yet) + if (cy.batching()) { + return this; + } - if (start < 0) { - start = thisSize + start; - } + var updated = []; - if (end < 0) { - end = thisSize + end; + function update(parent) { + if (!parent.isParent()) { + return; } - for (var i = start; i >= 0 && i < end && i < thisSize; i++) { - array.push(this[i]); - } + var _p = parent._private; + var children = parent.children(); + var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include'; - return this.spawn(array); - }, + var min = { + width: { + val: parent.pstyle('min-width').pfValue, + left: parent.pstyle('min-width-bias-left'), + right: parent.pstyle('min-width-bias-right') + }, + height: { + val: parent.pstyle('min-height').pfValue, + top: parent.pstyle('min-height-bias-top'), + bottom: parent.pstyle('min-height-bias-bottom') + } + }; - size: function size() { - return this.length; - }, + var bb = children.boundingBox({ + includeLabels: includeLabels, + includeOverlays: false, - eq: function eq(i) { - return this[i] || this.spawn(); - }, + // updating the compound bounds happens outside of the regular + // cache cycle (i.e. before fired events) + useCache: false + }); + var pos = _p.position; - first: function first() { - return this[0] || this.spawn(); - }, + // if children take up zero area then keep position and fall back on stylesheet w/h + if (bb.w === 0 || bb.h === 0) { + bb = { + w: parent.pstyle('width').pfValue, + h: parent.pstyle('height').pfValue + }; - last: function last() { - return this[this.length - 1] || this.spawn(); - }, - - empty: function empty() { - return this.length === 0; - }, + bb.x1 = pos.x - bb.w / 2; + bb.x2 = pos.x + bb.w / 2; + bb.y1 = pos.y - bb.h / 2; + bb.y2 = pos.y + bb.h / 2; + } - nonempty: function nonempty() { - return !this.empty(); - }, + function computeBiasValues(propDiff, propBias, propBiasComplement) { + var biasDiff = 0; + var biasComplementDiff = 0; + var biasTotal = propBias + propBiasComplement; - sort: function sort(sortFn) { - if (!is.fn(sortFn)) { - return this; + if (propDiff > 0 && biasTotal > 0) { + biasDiff = propBias / biasTotal * propDiff; + biasComplementDiff = propBiasComplement / biasTotal * propDiff; + } + return { + biasDiff: biasDiff, + biasComplementDiff: biasComplementDiff + }; } - var sorted = this.toArray().sort(sortFn); + function computePaddingValues(width, height, paddingObject, relativeTo) { + // Assuming percentage is number from 0 to 1 + if (paddingObject.units === '%') { + switch (relativeTo) { + case 'width': + return width > 0 ? paddingObject.pfValue * width : 0; + case 'height': + return height > 0 ? paddingObject.pfValue * height : 0; + case 'average': + return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0; + case 'min': + return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0; + case 'max': + return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0; + default: + return 0; + } + } else if (paddingObject.units === 'px') { + return paddingObject.pfValue; + } else { + return 0; + } + } - return this.spawn(sorted); - }, + var leftVal = min.width.left.value; + if (min.width.left.units === 'px' && min.width.val > 0) { + leftVal = leftVal * 100 / min.width.val; + } + var rightVal = min.width.right.value; + if (min.width.right.units === 'px' && min.width.val > 0) { + rightVal = rightVal * 100 / min.width.val; + } - sortByZIndex: function sortByZIndex() { - return this.sort(zIndexSort); - }, + var topVal = min.height.top.value; + if (min.height.top.units === 'px' && min.height.val > 0) { + topVal = topVal * 100 / min.height.val; + } - zDepth: function zDepth() { - var ele = this[0]; - if (!ele) { - return undefined; + var bottomVal = min.height.bottom.value; + if (min.height.bottom.units === 'px' && min.height.val > 0) { + bottomVal = bottomVal * 100 / min.height.val; } - // let cy = ele.cy(); + var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal); + var diffLeft = widthBiasDiffs.biasDiff; + var diffRight = widthBiasDiffs.biasComplementDiff; + + var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal); + var diffTop = heightBiasDiffs.biasDiff; + var diffBottom = heightBiasDiffs.biasComplementDiff; + + _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value); + + _p.autoWidth = Math.max(bb.w, min.width.val); + pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2; + + _p.autoHeight = Math.max(bb.h, min.height.val); + pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2; + + updated.push(parent); + } + + for (var i = 0; i < this.length; i++) { + var ele = this[i]; var _p = ele._private; - var group = _p.group; - if (group === 'nodes') { - var depth = _p.data.parent ? ele.parents().size() : 0; + if (!_p.compoundBoundsClean) { + update(ele); - if (!ele.isParent()) { - return util.MAX_INT - 1; // childless nodes always on top + if (!cy._private.batchingStyle) { + _p.compoundBoundsClean = true; } + } + } - return depth; - } else { - var src = _p.source; - var tgt = _p.target; - var srcDepth = src.zDepth(); - var tgtDepth = tgt.zDepth(); + return this; +}; - return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent - } +var noninf = function noninf(x) { + if (x === Infinity || x === -Infinity) { + return 0; } + + return x; }; -elesfn.each = elesfn.forEach; +var updateBounds = function updateBounds(b, x1, y1, x2, y2) { + // don't update with zero area boxes + if (x2 - x1 === 0 || y2 - y1 === 0) { + return; + } -module.exports = elesfn; + // don't update with null dim + if (x1 == null || y1 == null || x2 == null || y2 == null) { + return; + } -/***/ }), -/* 51 */ -/***/ (function(module, exports, __webpack_require__) { + b.x1 = x1 < b.x1 ? x1 : b.x1; + b.x2 = x2 > b.x2 ? x2 : b.x2; + b.y1 = y1 < b.y1 ? y1 : b.y1; + b.y2 = y2 > b.y2 ? y2 : b.y2; +}; -"use strict"; +var updateBoundsFromBox = function updateBoundsFromBox(b, b2) { + return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2); +}; +var prefixedProperty = function prefixedProperty(obj, field, prefix) { + return util.getPrefixedProperty(obj, field, prefix); +}; -var is = __webpack_require__(0); -var util = __webpack_require__(1); -var Promise = __webpack_require__(5); -var math = __webpack_require__(2); +var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) { + if (ele.cy().headless()) { + return; + } -var elesfn = { - // Calculates and returns node dimensions { x, y } based on options given - layoutDimensions: function layoutDimensions(options) { - options = util.assign({ - nodeDimensionsIncludeLabels: true - }, options); + var _p = ele._private; + var rstyle = _p.rstyle; + var halfArW = rstyle.arrowWidth / 2; + var arrowType = ele.pstyle(prefix + '-arrow-shape').value; + var x = void 0; + var y = void 0; - if (options.nodeDimensionsIncludeLabels) { - var bbDim = this.boundingBox(); - return { - w: bbDim.w, - h: bbDim.h - }; + if (arrowType !== 'none') { + if (prefix === 'source') { + x = rstyle.srcX; + y = rstyle.srcY; + } else if (prefix === 'target') { + x = rstyle.tgtX; + y = rstyle.tgtY; } else { - return { - w: this.outerWidth(), - h: this.outerHeight() - }; + x = rstyle.midX; + y = rstyle.midY; } - }, - // using standard layout options, apply position function (w/ or w/o animation) - layoutPositions: function layoutPositions(layout, options, fn) { - var nodes = this.nodes(); - var cy = this.cy(); - var layoutEles = options.eles; // nodes & edges - var getMemoizeKey = function getMemoizeKey(node, i) { - return node.id() + '$' + i; - }; - var fnMem = util.memoize(fn, getMemoizeKey); // memoized version of position function + updateBounds(bounds, x - halfArW, y - halfArW, x + halfArW, y + halfArW); + } +}; - layout.emit({ type: 'layoutstart', layout: layout }); +var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) { + if (ele.cy().headless()) { + return; + } - layout.animations = []; + var prefixDash = void 0; - var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) { - var center = { - x: nodesBb.x1 + nodesBb.w / 2, - y: nodesBb.y1 + nodesBb.h / 2 - }; + if (prefix) { + prefixDash = prefix + '-'; + } else { + prefixDash = ''; + } - var spacingVector = { // scale from center of bounding box (not necessarily 0,0) - x: (pos.x - center.x) * spacing, - y: (pos.y - center.y) * spacing - }; + var _p = ele._private; + var rstyle = _p.rstyle; + var label = ele.pstyle(prefixDash + 'label').strValue; - return { - x: center.x + spacingVector.x, - y: center.y + spacingVector.y - }; - }; + if (label) { + var halign = ele.pstyle('text-halign'); + var valign = ele.pstyle('text-valign'); + var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix); + var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix); + var labelX = prefixedProperty(rstyle, 'labelX', prefix); + var labelY = prefixedProperty(rstyle, 'labelY', prefix); + var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue; + var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue; + var isEdge = ele.isEdge(); + var rotation = ele.pstyle(prefixDash + 'text-rotation'); + var outlineWidth = ele.pstyle('text-outline-width').pfValue; + var borderWidth = ele.pstyle('text-border-width').pfValue; + var halfBorderWidth = borderWidth / 2; + var padding = ele.pstyle('text-background-padding').pfValue; - var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1; + var lh = labelHeight + 2 * padding; + var lw = labelWidth + 2 * padding; + var lw_2 = lw / 2; + var lh_2 = lh / 2; + var lx1 = void 0, + lx2 = void 0, + ly1 = void 0, + ly2 = void 0; - var spacingBb = function spacingBb() { - if (!useSpacingFactor) { - return null; - } - - var bb = math.makeBoundingBox(); + if (isEdge) { + lx1 = labelX - lw_2; + lx2 = labelX + lw_2; + ly1 = labelY - lh_2; + ly2 = labelY + lh_2; + } else { + switch (halign.value) { + case 'left': + lx1 = labelX - lw; + lx2 = labelX; + break; - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var pos = fnMem(node, i); + case 'center': + lx1 = labelX - lw_2; + lx2 = labelX + lw_2; + break; - math.expandBoundingBoxByPoint(bb, pos.x, pos.y); + case 'right': + lx1 = labelX; + lx2 = labelX + lw; + break; } - return bb; - }; - - var bb = spacingBb(); - - var getFinalPos = util.memoize(function (node, i) { - var newPos = fnMem(node, i); - var pos = node.position(); - - if (!is.number(pos.x) || !is.number(pos.y)) { - node.silentPosition({ x: 0, y: 0 }); - } + switch (valign.value) { + case 'top': + ly1 = labelY - lh; + ly2 = labelY; + break; - if (useSpacingFactor) { - var spacing = Math.abs(options.spacingFactor); + case 'center': + ly1 = labelY - lh_2; + ly2 = labelY + lh_2; + break; - newPos = calculateSpacing(spacing, bb, newPos); + case 'bottom': + ly1 = labelY; + ly2 = labelY + lh; + break; } + } - if (options.transform != null) { - newPos = options.transform(node, newPos); - } + var isAutorotate = isEdge && rotation.strValue === 'autorotate'; + var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0; - return newPos; - }, getMemoizeKey); + if (isAutorotate || isPfValue) { + var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue; + var cos = Math.cos(theta); + var sin = Math.sin(theta); - if (options.animate) { - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var newPos = getFinalPos(node, i); - var animateNode = options.animateFilter == null || options.animateFilter(node, i); + var rotate = function rotate(x, y) { + x = x - labelX; + y = y - labelY; - if (animateNode) { - var ani = node.animation({ - position: newPos, - duration: options.animationDuration, - easing: options.animationEasing - }); + return { + x: x * cos - y * sin + labelX, + y: x * sin + y * cos + labelY + }; + }; - layout.animations.push(ani); + var px1y1 = rotate(lx1, ly1); + var px1y2 = rotate(lx1, ly2); + var px2y1 = rotate(lx2, ly1); + var px2y2 = rotate(lx2, ly2); - ani.play(); - } else { - node.position(newPos); - } - } + lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x); + lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x); + ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y); + ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y); + } - if (options.fit) { - var fitAni = cy.animation({ - fit: { - boundingBox: layoutEles.boundingBoxAt(getFinalPos), - padding: options.padding - }, - duration: options.animationDuration, - easing: options.animationEasing - }); + lx1 += marginX - Math.max(outlineWidth, halfBorderWidth); + lx2 += marginX + Math.max(outlineWidth, halfBorderWidth); + ly1 += marginY - Math.max(outlineWidth, halfBorderWidth); + ly2 += marginY + Math.max(outlineWidth, halfBorderWidth); - layout.animations.push(fitAni); + updateBounds(bounds, lx1, ly1, lx2, ly2); + } - fitAni.play(); - } else if (options.zoom !== undefined && options.pan !== undefined) { - var zoomPanAni = cy.animation({ - zoom: options.zoom, - pan: options.pan, - duration: options.animationDuration, - easing: options.animationEasing - }); + return bounds; +}; - layout.animations.push(zoomPanAni); +// get the bounding box of the elements (in raw model position) +var boundingBoxImpl = function boundingBoxImpl(ele, options) { + var cy = ele._private.cy; + var styleEnabled = cy.styleEnabled(); + var headless = cy.headless(); - zoomPanAni.play(); - } + var bounds = { + x1: Infinity, + y1: Infinity, + x2: -Infinity, + y2: -Infinity + }; - layout.one('layoutready', options.ready); - layout.emit({ type: 'layoutready', layout: layout }); + var _p = ele._private; + var display = styleEnabled ? ele.pstyle('display').value : 'element'; + var isNode = ele.isNode(); + var isEdge = ele.isEdge(); + var ex1 = void 0, + ex2 = void 0, + ey1 = void 0, + ey2 = void 0; // extrema of body / lines + var x = void 0, + y = void 0; // node pos + var displayed = display !== 'none'; - Promise.all(layout.animations.map(function (ani) { - return ani.promise(); - })).then(function () { - layout.one('layoutstop', options.stop); - layout.emit({ type: 'layoutstop', layout: layout }); - }); - } else { + if (displayed) { + var overlayOpacity = 0; + var overlayPadding = 0; - nodes.positions(getFinalPos); + if (styleEnabled && options.includeOverlays) { + overlayOpacity = ele.pstyle('overlay-opacity').value; - if (options.fit) { - cy.fit(options.eles, options.padding); + if (overlayOpacity !== 0) { + overlayPadding = ele.pstyle('overlay-padding').value; } + } - if (options.zoom != null) { - cy.zoom(options.zoom); - } + var w = 0; + var wHalf = 0; - if (options.pan) { - cy.pan(options.pan); - } + if (styleEnabled) { + w = ele.pstyle('width').pfValue; + wHalf = w / 2; + } - layout.one('layoutready', options.ready); - layout.emit({ type: 'layoutready', layout: layout }); + if (isNode && options.includeNodes) { + var pos = ele.position(); + x = pos.x; + y = pos.y; + var _w = ele.outerWidth(); + var halfW = _w / 2; + var h = ele.outerHeight(); + var halfH = h / 2; - layout.one('layoutstop', options.stop); - layout.emit({ type: 'layoutstop', layout: layout }); - } + // handle node dimensions + ///////////////////////// - return this; // chaining - }, + ex1 = x - halfW - overlayPadding; + ex2 = x + halfW + overlayPadding; + ey1 = y - halfH - overlayPadding; + ey2 = y + halfH + overlayPadding; - layout: function layout(options) { - var cy = this.cy(); + updateBounds(bounds, ex1, ey1, ex2, ey2); + } else if (isEdge && options.includeEdges) { + var rstyle = _p.rstyle || {}; - return cy.makeLayout(util.extend({}, options, { - eles: this - })); - } + // handle edge dimensions (rough box estimate) + ////////////////////////////////////////////// + if (styleEnabled && !headless) { + ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX); + ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX); + ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY); + ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); -}; + // take into account edge width + ex1 -= wHalf; + ex2 += wHalf; + ey1 -= wHalf; + ey2 += wHalf; -// aliases: -elesfn.createLayout = elesfn.makeLayout = elesfn.layout; + updateBounds(bounds, ex1, ey1, ex2, ey2); + } -module.exports = elesfn; + // precise haystacks + //////////////////// + if (styleEnabled && !headless && ele.pstyle('curve-style').strValue === 'haystack') { + var hpts = rstyle.haystackPts || []; -/***/ }), -/* 52 */ -/***/ (function(module, exports, __webpack_require__) { + ex1 = hpts[0].x; + ey1 = hpts[0].y; + ex2 = hpts[1].x; + ey2 = hpts[1].y; -"use strict"; + if (ex1 > ex2) { + var temp = ex1; + ex1 = ex2; + ex2 = temp; + } + if (ey1 > ey2) { + var _temp = ey1; + ey1 = ey2; + ey2 = _temp; + } -var is = __webpack_require__(0); + updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf); -function styleCache(key, fn, ele) { - var _p = ele._private; - var cache = _p.styleCache = _p.styleCache || {}; - var val; - - if ((val = cache[key]) != null) { - return val; - } else { - val = cache[key] = fn(ele); - - return val; - } -} - -function cacheStyleFunction(key, fn) { - return function cachedStyleFunction(ele) { - return styleCache(key, fn, ele); - }; -} + // handle points along edge + /////////////////////////// + } else { + var pts = rstyle.bezierPts || rstyle.linePts || []; -function cachePrototypeStyleFunction(key, fn) { - var selfFn = function selfFn(ele) { - return fn.call(ele); - }; + for (var j = 0; j < pts.length; j++) { + var pt = pts[j]; - return function cachedPrototypeStyleFunction() { - var ele = this[0]; + ex1 = pt.x - wHalf; + ex2 = pt.x + wHalf; + ey1 = pt.y - wHalf; + ey2 = pt.y + wHalf; - if (ele) { - return styleCache(key, selfFn, ele); - } - }; -} + updateBounds(bounds, ex1, ey1, ex2, ey2); + } -var elesfn = { + // fallback on source and target positions + ////////////////////////////////////////// + if (pts.length === 0) { + var n1 = ele.source(); + var n1pos = n1.position(); - recalculateRenderedStyle: function recalculateRenderedStyle(useCache) { - var cy = this.cy(); - var renderer = cy.renderer(); - var styleEnabled = cy.styleEnabled(); + var n2 = ele.target(); + var n2pos = n2.position(); - if (renderer && styleEnabled) { - renderer.recalculateRenderedStyle(this, useCache); - } + ex1 = n1pos.x; + ex2 = n2pos.x; + ey1 = n1pos.y; + ey2 = n2pos.y; - return this; - }, + if (ex1 > ex2) { + var _temp2 = ex1; + ex1 = ex2; + ex2 = _temp2; + } - dirtyStyleCache: function dirtyStyleCache() { - var cy = this.cy(); - var dirty = function dirty(ele) { - return ele._private.styleCache = {}; - }; + if (ey1 > ey2) { + var _temp3 = ey1; + ey1 = ey2; + ey2 = _temp3; + } - if (cy.hasCompoundNodes()) { - var eles = void 0; + // take into account edge width + ex1 -= wHalf; + ex2 += wHalf; + ey1 -= wHalf; + ey2 += wHalf; - eles = this.spawnSelf().merge(this.descendants()).merge(this.parents()); + updateBounds(bounds, ex1, ey1, ex2, ey2); + } + } + } // edges - eles.merge(eles.connectedEdges()); - eles.forEach(dirty); - } else { - this.forEach(function (ele) { - dirty(ele); + // handle edge arrow size + ///////////////////////// - ele.connectedEdges().forEach(dirty); - }); + if (styleEnabled && options.includeEdges && isEdge) { + updateBoundsFromArrow(bounds, ele, 'mid-source', options); + updateBoundsFromArrow(bounds, ele, 'mid-target', options); + updateBoundsFromArrow(bounds, ele, 'source', options); + updateBoundsFromArrow(bounds, ele, 'target', options); } - return this; - }, - - // fully updates (recalculates) the style for the elements - updateStyle: function updateStyle(notifyRenderer) { - var cy = this._private.cy; - - if (!cy.styleEnabled()) { - return this; - } + // ghost + //////// - if (cy._private.batchingStyle) { - var bEles = cy._private.batchStyleEles; + if (styleEnabled) { + var ghost = ele.pstyle('ghost').value === 'yes'; - bEles.merge(this); + if (ghost) { + var gx = ele.pstyle('ghost-offset-x').pfValue; + var gy = ele.pstyle('ghost-offset-y').pfValue; - return this; // chaining and exit early when batching + updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy); + } } - var hasCompounds = cy.hasCompoundNodes(); - var style = cy.style(); - var updatedEles = this; - - notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false; - - if (hasCompounds) { - // then add everything up and down for compound selector checks - updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents()); - } + // overlay + ////////// - var changedEles = style.apply(updatedEles); + if (styleEnabled) { - changedEles.dirtyStyleCache(); - changedEles.dirtyCompoundBoundsCache(); + ex1 = bounds.x1; + ex2 = bounds.x2; + ey1 = bounds.y1; + ey2 = bounds.y2; - if (notifyRenderer) { - changedEles.emitAndNotify('style'); // let renderer know we changed style - } else { - changedEles.emit('style'); // just fire the event + updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding); } - return this; // chaining - }, + // handle label dimensions + ////////////////////////// - // just update the mappers in the elements' styles; cheaper than eles.updateStyle() - updateMappers: function updateMappers(notifyRenderer) { - var cy = this._private.cy; - var style = cy.style(); - notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false; + if (styleEnabled && options.includeLabels) { + updateBoundsFromLabel(bounds, ele, null, options); - if (!cy.styleEnabled()) { - return this; - } + if (isEdge) { + updateBoundsFromLabel(bounds, ele, 'source', options); + updateBoundsFromLabel(bounds, ele, 'target', options); + } + } // style enabled for labels + } // if displayed - var changedEles = style.updateMappers(this); + bounds.x1 = noninf(bounds.x1); + bounds.y1 = noninf(bounds.y1); + bounds.x2 = noninf(bounds.x2); + bounds.y2 = noninf(bounds.y2); + bounds.w = noninf(bounds.x2 - bounds.x1); + bounds.h = noninf(bounds.y2 - bounds.y1); - changedEles.dirtyStyleCache(); - changedEles.dirtyCompoundBoundsCache(); + // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides + if (bounds.w > 0 && bounds.h > 0 && displayed) { + math.expandBoundingBox(bounds, 1); + } - if (notifyRenderer) { - changedEles.emitAndNotify('style'); // let renderer know we changed style - } else { - changedEles.emit('style'); // just fire the event - } - return this; // chaining - }, + return bounds; +}; - // get the internal parsed style object for the specified property - parsedStyle: function parsedStyle(property) { - var ele = this[0]; - var cy = ele.cy(); +var tf = function tf(val) { + if (val) { + return 't'; + } else { + return 'f'; + } +}; - if (!cy.styleEnabled()) { - return; - } +var getKey = function getKey(opts) { + var key = ''; - if (ele) { - return ele._private.style[property] || cy.style().getDefaultProperty(property); - } - }, + key += tf(opts.incudeNodes); + key += tf(opts.includeEdges); + key += tf(opts.includeLabels); + key += tf(opts.includeOverlays); - numericStyle: function numericStyle(property) { - var ele = this[0]; + return key; +}; - if (!ele.cy().styleEnabled()) { - return; - } +var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) { + var _p = ele._private; + var bb = void 0; + var headless = ele.cy().headless(); + var key = opts === defBbOpts ? defBbOptsKey : getKey(opts); - if (ele) { - var pstyle = ele.pstyle(property); + if (!opts.useCache || headless || !_p.bbCache || !_p.bbCache[key]) { + bb = boundingBoxImpl(ele, opts); - return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value; + if (!headless) { + _p.bbCache = _p.bbCache || {}; + _p.bbCache[key] = bb; } - }, + } else { + bb = _p.bbCache[key]; + } - numericStyleUnits: function numericStyleUnits(property) { - var ele = this[0]; + return bb; +}; - if (!ele.cy().styleEnabled()) { - return; - } +var defBbOpts = { + includeNodes: true, + includeEdges: true, + includeLabels: true, + includeOverlays: true, + useCache: true +}; - if (ele) { - return ele.pstyle(property).units; - } - }, - - // get the specified css property as a rendered value (i.e. on-screen value) - // or get the whole rendered style if no property specified (NB doesn't allow setting) - renderedStyle: function renderedStyle(property) { - var cy = this.cy(); - if (!cy.styleEnabled()) { - return this; - } +var defBbOptsKey = getKey(defBbOpts); - var ele = this[0]; +function filledBbOpts(options) { + return { + includeNodes: util.default(options.includeNodes, defBbOpts.includeNodes), + includeEdges: util.default(options.includeEdges, defBbOpts.includeEdges), + includeLabels: util.default(options.includeLabels, defBbOpts.includeLabels), + includeOverlays: util.default(options.includeOverlays, defBbOpts.includeOverlays), + useCache: util.default(options.useCache, defBbOpts.useCache) + }; +} - if (ele) { - return cy.style().getRenderedStyle(ele, property); +elesfn.boundingBox = function (options) { + // the main usecase is ele.boundingBox() for a single element with no/def options + // specified s.t. the cache is used, so check for this case to make it faster by + // avoiding the overhead of the rest of the function + if (this.length === 1 && this[0]._private.bbCache && (options === undefined || options.useCache === undefined || options.useCache === true)) { + if (options === undefined) { + options = defBbOpts; + } else { + options = filledBbOpts(options); } - }, - // read the calculated css style of the element or override the style (via a bypass) - style: function style(name, value) { - var cy = this.cy(); + return cachedBoundingBoxImpl(this[0], options); + } - if (!cy.styleEnabled()) { - return this; - } + var bounds = { + x1: Infinity, + y1: Infinity, + x2: -Infinity, + y2: -Infinity + }; - var updateTransitions = false; - var style = cy.style(); + options = options || util.staticEmptyObject(); - if (is.plainObject(name)) { - // then extend the bypass - var props = name; - style.applyBypass(this, props, updateTransitions); + var opts = filledBbOpts(options); - this.dirtyStyleCache(); - this.dirtyCompoundBoundsCache(); + var eles = this; + var cy = eles.cy(); + var styleEnabled = cy.styleEnabled(); - this.emitAndNotify('style'); // let the renderer know we've updated style - } else if (is.string(name)) { + if (styleEnabled) { + this.recalculateRenderedStyle(opts.useCache); + } - if (value === undefined) { - // then get the property from the style - var ele = this[0]; + this.updateCompoundBounds(); - if (ele) { - return style.getStylePropertyValue(ele, name); - } else { - // empty collection => can't get any value - return; - } - } else { - // then set the bypass with the property value - style.applyBypass(this, name, value, updateTransitions); + var updatedEdge = {}; // use to avoid duplicated edge updates - this.dirtyStyleCache(); - this.dirtyCompoundBoundsCache(); + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - this.emitAndNotify('style'); // let the renderer know we've updated style - } - } else if (name === undefined) { - var _ele = this[0]; + if (styleEnabled && ele.isEdge() && ele.pstyle('curve-style').strValue === 'bezier' && !updatedEdge[ele.id()]) { + var edges = ele.parallelEdges(); - if (_ele) { - return style.getRawStyle(_ele); - } else { - // empty collection => can't get any value - return; + for (var j = 0; j < edges.length; j++) { + // make all as updated + updatedEdge[edges[j].id()] = true; } + + edges.recalculateRenderedStyle(opts.useCache); // n.b. ele.parallelEdges() single is cached } - return this; // chaining - }, + updateBoundsFromBox(bounds, cachedBoundingBoxImpl(ele, opts)); + } - removeStyle: function removeStyle(names) { - var cy = this.cy(); + bounds.x1 = noninf(bounds.x1); + bounds.y1 = noninf(bounds.y1); + bounds.x2 = noninf(bounds.x2); + bounds.y2 = noninf(bounds.y2); + bounds.w = noninf(bounds.x2 - bounds.x1); + bounds.h = noninf(bounds.y2 - bounds.y1); - if (!cy.styleEnabled()) { - return this; - } + return bounds; +}; - var updateTransitions = false; - var style = cy.style(); - var eles = this; +// private helper to get bounding box for custom node positions +// - good for perf in certain cases but currently requires dirtying the rendered style +// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer... +// - try to use for only things like discrete layouts where the node position would change anyway +elesfn.boundingBoxAt = function (fn) { + var nodes = this.nodes(); - if (names === undefined) { - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; + if (is.plainObject(fn)) { + var obj = fn; - style.removeAllBypasses(ele, updateTransitions); - } - } else { - names = names.split(/\s+/); + fn = function fn() { + return obj; + }; + } - for (var _i = 0; _i < eles.length; _i++) { - var _ele2 = eles[_i]; + // save the current position and set the new one, per node + for (var i = 0; i < nodes.length; i++) { + var n = nodes[i]; + var _p = n._private; + var pos = _p.position; + var newPos = fn.call(n, n, i); - style.removeBypasses(_ele2, names, updateTransitions); - } + _p.bbAtOldPos = { x: pos.x, y: pos.y }; + + if (newPos) { + pos.x = newPos.x; + pos.y = newPos.y; } + } - this.dirtyStyleCache(); - this.dirtyCompoundBoundsCache(); + this.emit('dirty'); // let the renderer know we've manually dirtied rendered dim calcs - this.emitAndNotify('style'); // let the renderer know we've updated style + nodes.dirtyCompoundBoundsCache().updateCompoundBounds(); - return this; // chaining - }, + var bb = this.boundingBox({ useCache: false }); - show: function show() { - this.css('display', 'element'); - return this; // chaining - }, + // restore the original position, per node + for (var _i = 0; _i < nodes.length; _i++) { + var _n = nodes[_i]; + var _p2 = _n._private; + var _pos = _n._private.position; + var old = _p2.bbAtOldPos; - hide: function hide() { - this.css('display', 'none'); - return this; // chaining - }, + _pos.x = old.x; + _pos.y = old.y; + } - effectiveOpacity: function effectiveOpacity() { - var cy = this.cy(); - if (!cy.styleEnabled()) { - return 1; - } + nodes.dirtyCompoundBoundsCache(); - var hasCompoundNodes = cy.hasCompoundNodes(); - var ele = this[0]; + this.emit('dirty'); // let the renderer know we've manually dirtied rendered dim calcs - if (ele) { - var _p = ele._private; - var parentOpacity = ele.pstyle('opacity').value; + return bb; +}; - if (!hasCompoundNodes) { - return parentOpacity; - } +fn.boundingbox = fn.boundingBox; +fn.renderedBoundingbox = fn.renderedBoundingBox; - var parents = !_p.data.parent ? null : ele.parents(); +module.exports = elesfn; - if (parents) { - for (var i = 0; i < parents.length; i++) { - var parent = parents[i]; - var opacity = parent.pstyle('opacity').value; +/***/ }), +/* 59 */ +/***/ (function(module, exports, __webpack_require__) { - parentOpacity = opacity * parentOpacity; - } - } +"use strict"; - return parentOpacity; - } - }, - transparent: function transparent() { - var cy = this.cy(); - if (!cy.styleEnabled()) { - return false; - } +var util = __webpack_require__(1); +var fn = void 0, + elesfn = void 0; +fn = elesfn = {}; + +var defineDimFns = function defineDimFns(opts) { + opts.uppercaseName = util.capitalize(opts.name); + opts.autoName = 'auto' + opts.uppercaseName; + opts.labelName = 'label' + opts.uppercaseName; + opts.outerName = 'outer' + opts.uppercaseName; + opts.uppercaseOuterName = util.capitalize(opts.outerName); + + fn[opts.name] = function dimImpl() { var ele = this[0]; - var hasCompoundNodes = ele.cy().hasCompoundNodes(); + var _p = ele._private; + var cy = _p.cy; + var styleEnabled = cy._private.styleEnabled; if (ele) { - if (!hasCompoundNodes) { - return ele.pstyle('opacity').value === 0; - } else { - return ele.effectiveOpacity() === 0; - } - } - }, + if (styleEnabled) { + if (ele.isParent()) { + ele.updateCompoundBounds(); - backgrounding: function backgrounding() { - var cy = this.cy(); - if (!cy.styleEnabled()) { - return false; - } + return _p[opts.autoName] || 0; + } - var ele = this[0]; - - return ele._private.backgrounding ? true : false; - } - -}; + var d = ele.pstyle(opts.name); -function checkCompound(ele, parentOk) { - var _p = ele._private; - var parents = _p.data.parent ? ele.parents() : null; + switch (d.strValue) { + case 'label': + ele.recalculateRenderedStyle(); - if (parents) { - for (var i = 0; i < parents.length; i++) { - var parent = parents[i]; + return _p.rstyle[opts.labelName] || 0; - if (!parentOk(parent)) { - return false; + default: + return d.pfValue; + } + } else { + return 1; } } - } - - return true; -} - -function defineDerivedStateFunction(specs) { - var ok = specs.ok; - var edgeOkViaNode = specs.edgeOkViaNode || specs.ok; - var parentOk = specs.parentOk || specs.ok; - - return function () { - var cy = this.cy(); - if (!cy.styleEnabled()) { - return true; - } + }; + fn['outer' + opts.uppercaseName] = function outerDimImpl() { var ele = this[0]; - var hasCompoundNodes = cy.hasCompoundNodes(); + var _p = ele._private; + var cy = _p.cy; + var styleEnabled = cy._private.styleEnabled; if (ele) { - var _p = ele._private; - - if (!ok(ele)) { - return false; - } + if (styleEnabled) { + var dim = ele[opts.name](); + var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side + var padding = 2 * ele.padding(); - if (ele.isNode()) { - return !hasCompoundNodes || checkCompound(ele, parentOk); + return dim + border + padding; } else { - var src = _p.source; - var tgt = _p.target; - - return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode))); + return 1; } } }; -} - -var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) { - return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true); -}); - -elesfn.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({ - ok: eleTakesUpSpace -})); - -var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) { - return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele); -}); -var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) { - return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent); -}); + fn['rendered' + opts.uppercaseName] = function renderedDimImpl() { + var ele = this[0]; -elesfn.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({ - ok: eleInteractive, - parentOk: parentInteractive, - edgeOkViaNode: eleTakesUpSpace -})); + if (ele) { + var d = ele[opts.name](); + return d * this.cy().zoom(); + } + }; -elesfn.noninteractive = function () { - var ele = this[0]; + fn['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() { + var ele = this[0]; - if (ele) { - return !ele.interactive(); - } + if (ele) { + var od = ele[opts.outerName](); + return od * this.cy().zoom(); + } + }; }; -var eleVisible = cacheStyleFunction('eleVisible', function (ele) { - return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele); +defineDimFns({ + name: 'width' }); -var edgeVisibleViaNode = eleTakesUpSpace; - -elesfn.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({ - ok: eleVisible, - edgeOkViaNode: edgeVisibleViaNode -})); +defineDimFns({ + name: 'height' +}); -elesfn.hidden = function () { +elesfn.padding = function () { var ele = this[0]; + var _p = ele._private; + if (ele.isParent()) { + ele.updateCompoundBounds(); - if (ele) { - return !ele.visible(); + if (_p.autoPadding !== undefined) { + return _p.autoPadding; + } else { + return ele.pstyle('padding').pfValue; + } + } else { + return ele.pstyle('padding').pfValue; } }; -elesfn.bypass = elesfn.css = elesfn.style; -elesfn.renderedCss = elesfn.renderedStyle; -elesfn.removeBypass = elesfn.removeCss = elesfn.removeStyle; -elesfn.pstyle = elesfn.parsedStyle; - module.exports = elesfn; /***/ }), -/* 53 */ +/* 60 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var elesfn = {}; - -function defineSwitchFunction(params) { - return function () { - var args = arguments; - var changedEles = []; - - // e.g. cy.nodes().select( data, handler ) - if (args.length === 2) { - var data = args[0]; - var handler = args[1]; - this.on(params.event, data, handler); - } - - // e.g. cy.nodes().select( handler ) - else if (args.length === 1) { - var _handler = args[0]; - this.on(params.event, _handler); - } +var ifEdge = function ifEdge(self, then) { + if (self.isEdge()) { + return then(self.renderer()); + } +}; - // e.g. cy.nodes().select() - else if (args.length === 0) { - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - var able = !params.ableField || ele._private[params.ableField]; - var changed = ele._private[params.field] != params.value; +module.exports = { + controlPoints: function controlPoints() { + var _this = this; - if (params.overrideAble) { - var overrideAble = params.overrideAble(ele); + return ifEdge(this, function (renderer) { + return renderer.getControlPoints(_this); + }); + }, + segmentPoints: function segmentPoints() { + var _this2 = this; - if (overrideAble !== undefined) { - able = overrideAble; + return ifEdge(this, function (renderer) { + return renderer.getSegmentPoints(_this2); + }); + }, + sourceEndpoint: function sourceEndpoint() { + var _this3 = this; - if (!overrideAble) { - return this; - } // to save cycles assume not able for all on override - } - } + return ifEdge(this, function (renderer) { + return renderer.getSourceEndpoint(_this3); + }); + }, + targetEndpoint: function targetEndpoint() { + var _this4 = this; - if (able) { - ele._private[params.field] = params.value; + return ifEdge(this, function (renderer) { + return renderer.getTargetEndpoint(_this4); + }); + }, + midpoint: function midpoint() { + var _this5 = this; - if (changed) { - changedEles.push(ele); - } - } - } + return ifEdge(this, function (renderer) { + return renderer.getEdgeMidpoint(_this5); + }); + } +}; - var changedColl = this.spawn(changedEles); - changedColl.updateStyle(); // change of state => possible change of style - changedColl.emit(params.event); - } +/***/ }), +/* 61 */ +/***/ (function(module, exports, __webpack_require__) { - return this; - }; -} +"use strict"; -function defineSwitchSet(params) { - elesfn[params.field] = function () { - var ele = this[0]; - if (ele) { - if (params.overrideField) { - var val = params.overrideField(ele); +var Emitter = __webpack_require__(11); +var define = __webpack_require__(4); +var is = __webpack_require__(0); +var util = __webpack_require__(1); +var Selector = __webpack_require__(6); - if (val !== undefined) { - return val; - } - } +var emitterOptions = { + qualifierCompare: function qualifierCompare(selector1, selector2) { + if (selector1 == null || selector2 == null) { + return selector1 == null && selector2 == null; + } else { + return selector1.sameText(selector2); + } + }, + eventMatches: function eventMatches(ele, listener, eventObj) { + var selector = listener.qualifier; - return ele._private[params.field]; + if (selector != null) { + return ele !== eventObj.target && is.element(eventObj.target) && selector.matches(eventObj.target); } - }; - elesfn[params.on] = defineSwitchFunction({ - event: params.on, - field: params.field, - ableField: params.ableField, - overrideAble: params.overrideAble, - value: true - }); + return true; + }, + eventFields: function eventFields(ele) { + return { + cy: ele.cy(), + target: ele + }; + }, + callbackContext: function callbackContext(ele, listener, eventObj) { + return listener.qualifier != null ? eventObj.target : ele; + }, + beforeEmit: function beforeEmit(context, listener /*, eventObj*/) { + if (listener.conf && listener.conf.once) { + listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback); + } + }, + bubble: function bubble() { + return true; + }, + parent: function parent(ele) { + return ele.isChild() ? ele.parent() : ele.cy(); + } +}; - elesfn[params.off] = defineSwitchFunction({ - event: params.off, - field: params.field, - ableField: params.ableField, - overrideAble: params.overrideAble, - value: false - }); -} +var argSelector = function argSelector(arg) { + if (is.string(arg)) { + return new Selector(arg); + } else { + return arg; + } +}; -defineSwitchSet({ - field: 'locked', - overrideField: function overrideField(ele) { - return ele.cy().autolock() ? true : undefined; +var elesfn = { + createEmitter: function createEmitter() { + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + var _p = ele._private; + + if (!_p.emitter) { + _p.emitter = new Emitter(util.assign({ + context: ele + }, emitterOptions)); + } + } + + return this; }, - on: 'lock', - off: 'unlock' -}); -defineSwitchSet({ - field: 'grabbable', - overrideField: function overrideField(ele) { - return ele.cy().autoungrabify() ? false : undefined; + emitter: function emitter() { + return this._private.emitter; }, - on: 'grabify', - off: 'ungrabify' -}); -defineSwitchSet({ - field: 'selected', - ableField: 'selectable', - overrideAble: function overrideAble(ele) { - return ele.cy().autounselectify() ? false : undefined; + on: function on(events, selector, callback) { + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + + ele.emitter().on(events, argSelector(selector), callback); + } + + return this; }, - on: 'select', - off: 'unselect' -}); -defineSwitchSet({ - field: 'selectable', - overrideField: function overrideField(ele) { - return ele.cy().autounselectify() ? false : undefined; + removeListener: function removeListener(events, selector, callback) { + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + + ele.emitter().removeListener(events, argSelector(selector), callback); + } + + return this; }, - on: 'selectify', - off: 'unselectify' -}); -elesfn.deselect = elesfn.unselect; + one: function one(events, selector, callback) { + for (var i = 0; i < this.length; i++) { + var ele = this[i]; -elesfn.grabbed = function () { - var ele = this[0]; - if (ele) { - return ele._private.grabbed; - } -}; + ele.emitter().one(events, argSelector(selector), callback); + } -defineSwitchSet({ - field: 'active', - on: 'activate', - off: 'unactivate' -}); + return this; + }, -elesfn.inactive = function () { - var ele = this[0]; - if (ele) { - return !ele._private.active; + once: function once(events, selector, callback) { + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + + ele.emitter().on(events, argSelector(selector), callback, { + once: true, + onceCollection: this + }); + } + }, + + emit: function emit(events, extraParams) { + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + + ele.emitter().emit(events, extraParams); + } + + return this; + }, + + emitAndNotify: function emitAndNotify(event, extraParams) { + // for internal use only + if (this.length === 0) { + return; + } // empty collections don't need to notify anything + + // notify renderer + this.cy().notify({ + type: event, + eles: this + }); + + this.emit(event, extraParams); + + return this; } }; +define.eventAliasesOn(elesfn); + module.exports = elesfn; /***/ }), -/* 54 */ +/* 62 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var util = __webpack_require__(1); var is = __webpack_require__(0); +var Selector = __webpack_require__(6); -var elesfn = {}; +var elesfn = { + nodes: function nodes(selector) { + return this.filter(function (ele) { + return ele.isNode(); + }).filter(selector); + }, -var cache = function cache(fn, name) { - return function traversalCache(arg1, arg2, arg3, arg4) { - var selectorOrEles = arg1; - var eles = this; - var key = void 0; + edges: function edges(selector) { + return this.filter(function (ele) { + return ele.isEdge(); + }).filter(selector); + }, - if (selectorOrEles == null) { - key = 'null'; - } else if (is.elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) { - key = '#' + selectorOrEles.id(); - } + filter: function filter(_filter, thisArg) { + if (_filter === undefined) { + // check this first b/c it's the most common/performant case + return this; + } else if (is.string(_filter) || is.elementOrCollection(_filter)) { + return new Selector(_filter).filter(this); + } else if (is.fn(_filter)) { + var filterEles = this.spawn(); + var eles = this; - if (eles.length === 1 && key) { - var _p = eles[0]._private; - var tch = _p.traversalCache = _p.traversalCache || {}; - var ch = tch[name] = tch[name] || {}; - var cacheHit = ch[key]; + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles); - if (cacheHit) { - return cacheHit; - } else { - return ch[key] = fn.call(eles, arg1, arg2, arg3, arg4); + if (include) { + filterEles.merge(ele); + } } - } else { - return fn.call(eles, arg1, arg2, arg3, arg4); + + return filterEles; } - }; -}; -// DAG functions -//////////////// + return this.spawn(); // if not handled by above, give 'em an empty collection + }, -var defineDagExtremity = function defineDagExtremity(params) { - return function dagExtremityImpl(selector) { - var eles = this; - var ret = []; + not: function not(toRemove) { + if (!toRemove) { + return this; + } else { - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - if (!ele.isNode()) { - continue; + if (is.string(toRemove)) { + toRemove = this.filter(toRemove); } - var disqualified = false; - var edges = ele.connectedEdges(); + var elements = []; + var rMap = toRemove._private.map; - for (var j = 0; j < edges.length; j++) { - var edge = edges[j]; - var src = edge.source(); - var tgt = edge.target(); + for (var i = 0; i < this.length; i++) { + var element = this[i]; - if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) { - disqualified = true; - break; + var remove = rMap.has(element.id()); + if (!remove) { + elements.push(element); } } - if (!disqualified) { - ret.push(ele); - } + return this.spawn(elements); } + }, - return this.spawn(ret, { unique: true }).filter(selector); - }; -}; + absoluteComplement: function absoluteComplement() { + var cy = this.cy(); -var defineDagOneHop = function defineDagOneHop(params) { - return function (selector) { - var eles = this; - var oEles = []; + return cy.mutableElements().not(this); + }, - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; + intersect: function intersect(other) { + // if a selector is specified, then filter by it instead + if (is.string(other)) { + var selector = other; + return this.filter(selector); + } - if (!ele.isNode()) { - continue; - } + var elements = []; + var col1 = this; + var col2 = other; + var col1Smaller = this.length < other.length; + var map2 = col1Smaller ? col2._private.map : col1._private.map; + var col = col1Smaller ? col1 : col2; - var edges = ele.connectedEdges(); - for (var j = 0; j < edges.length; j++) { - var edge = edges[j]; - var src = edge.source(); - var tgt = edge.target(); + for (var i = 0; i < col.length; i++) { + var id = col[i]._private.data.id; + var entry = map2.get(id); - if (params.outgoing && src === ele) { - oEles.push(edge); - oEles.push(tgt); - } else if (params.incoming && tgt === ele) { - oEles.push(edge); - oEles.push(src); - } + if (entry) { + elements.push(entry.ele); } } - return this.spawn(oEles, { unique: true }).filter(selector); - }; -}; + return this.spawn(elements); + }, -var defineDagAllHops = function defineDagAllHops(params) { - return function (selector) { - var eles = this; - var sEles = []; - var sElesIds = {}; + xor: function xor(other) { + var cy = this._private.cy; - for (;;) { - var next = params.outgoing ? eles.outgoers() : eles.incomers(); + if (is.string(other)) { + other = cy.$(other); + } - if (next.length === 0) { - break; - } // done if none left + var elements = []; + var col1 = this; + var col2 = other; - var newNext = false; - for (var i = 0; i < next.length; i++) { - var n = next[i]; - var nid = n.id(); + var add = function add(col, other) { + for (var i = 0; i < col.length; i++) { + var ele = col[i]; + var id = ele._private.data.id; + var inOther = other.hasElementWithId(id); - if (!sElesIds[nid]) { - sElesIds[nid] = true; - sEles.push(n); - newNext = true; + if (!inOther) { + elements.push(ele); } } + }; - if (!newNext) { - break; - } // done if touched all outgoers already + add(col1, col2); + add(col2, col1); - eles = next; + return this.spawn(elements); + }, + + diff: function diff(other) { + var cy = this._private.cy; + + if (is.string(other)) { + other = cy.$(other); } - return this.spawn(sEles, { unique: true }).filter(selector); - }; -}; + var left = []; + var right = []; + var both = []; + var col1 = this; + var col2 = other; -elesfn.clearTraversalCache = function () { - for (var i = 0; i < this.length; i++) { - this[i]._private.traversalCache = null; - } -}; + var add = function add(col, other, retEles) { -util.extend(elesfn, { - // get the root nodes in the DAG - roots: defineDagExtremity({ noIncomingEdges: true }), + for (var i = 0; i < col.length; i++) { + var ele = col[i]; + var id = ele._private.data.id; + var inOther = other.hasElementWithId(id); - // get the leaf nodes in the DAG - leaves: defineDagExtremity({ noOutgoingEdges: true }), + if (inOther) { + both.push(ele); + } else { + retEles.push(ele); + } + } + }; - // normally called children in graph theory - // these nodes =edges=> outgoing nodes - outgoers: cache(defineDagOneHop({ outgoing: true }), 'outgoers'), + add(col1, col2, left); + add(col2, col1, right); - // aka DAG descendants - successors: defineDagAllHops({ outgoing: true }), + return { + left: this.spawn(left, { unique: true }), + right: this.spawn(right, { unique: true }), + both: this.spawn(both, { unique: true }) + }; + }, - // normally called parents in graph theory - // these nodes <=edges= incoming nodes - incomers: cache(defineDagOneHop({ incoming: true }), 'incomers'), + add: function add(toAdd) { + var cy = this._private.cy; - // aka DAG ancestors - predecessors: defineDagAllHops({ incoming: true }) -}); + if (!toAdd) { + return this; + } -// Neighbourhood functions -////////////////////////// + if (is.string(toAdd)) { + var selector = toAdd; + toAdd = cy.mutableElements().filter(selector); + } -util.extend(elesfn, { - neighborhood: cache(function (selector) { var elements = []; - var nodes = this.nodes(); - for (var i = 0; i < nodes.length; i++) { - // for all nodes - var node = nodes[i]; - var connectedEdges = node.connectedEdges(); + for (var i = 0; i < this.length; i++) { + elements.push(this[i]); + } - // for each connected edge, add the edge and the other node - for (var j = 0; j < connectedEdges.length; j++) { - var edge = connectedEdges[j]; - var src = edge.source(); - var tgt = edge.target(); - var otherNode = node === src ? tgt : src; + var map = this._private.map; - // need check in case of loop - if (otherNode.length > 0) { - elements.push(otherNode[0]); // add node 1 hop away - } + for (var _i = 0; _i < toAdd.length; _i++) { - // add connected edge - elements.push(edge[0]); + var add = !map.has(toAdd[_i].id()); + if (add) { + elements.push(toAdd[_i]); } } - return this.spawn(elements, { unique: true }).filter(selector); - }, 'neighborhood'), - - closedNeighborhood: function closedNeighborhood(selector) { - return this.neighborhood().add(this).filter(selector); + return this.spawn(elements); }, - openNeighborhood: function openNeighborhood(selector) { - return this.neighborhood(selector); - } -}); + // in place merge on calling collection + merge: function merge(toAdd) { + var _p = this._private; + var cy = _p.cy; -// aliases -elesfn.neighbourhood = elesfn.neighborhood; -elesfn.closedNeighbourhood = elesfn.closedNeighborhood; -elesfn.openNeighbourhood = elesfn.openNeighborhood; + if (!toAdd) { + return this; + } -// Edge functions -///////////////// + if (toAdd && is.string(toAdd)) { + var selector = toAdd; + toAdd = cy.mutableElements().filter(selector); + } -util.extend(elesfn, { - source: cache(function sourceImpl(selector) { - var ele = this[0]; - var src = void 0; + var map = _p.map; - if (ele) { - src = ele._private.source || ele.cy().collection(); - } + for (var i = 0; i < toAdd.length; i++) { + var toAddEle = toAdd[i]; + var id = toAddEle._private.data.id; + var add = !map.has(id); - return src && selector ? src.filter(selector) : src; - }, 'source'), + if (add) { + var index = this.length++; - target: cache(function targetImpl(selector) { - var ele = this[0]; - var tgt = void 0; + this[index] = toAddEle; - if (ele) { - tgt = ele._private.target || ele.cy().collection(); + map.set(id, { ele: toAddEle, index: index }); + } else { + // replace + var _index = map.get(id).index; + + this[_index] = toAddEle; + map.set(id, { ele: toAddEle, index: _index }); + } } - return tgt && selector ? tgt.filter(selector) : tgt; - }, 'target'), + return this; // chaining + }, - sources: defineSourceFunction({ - attr: 'source' - }), + // remove single ele in place in calling collection + unmergeOne: function unmergeOne(ele) { + ele = ele[0]; - targets: defineSourceFunction({ - attr: 'target' - }) -}); + var _p = this._private; + var id = ele._private.data.id; + var map = _p.map; + var entry = map.get(id); -function defineSourceFunction(params) { - return function sourceImpl(selector) { - var sources = []; + if (!entry) { + return this; // no need to remove + } - for (var i = 0; i < this.length; i++) { - var ele = this[i]; - var src = ele._private[params.attr]; + var i = entry.index; - if (src) { - sources.push(src); - } - } + // remove ele + this[i] = undefined; + map.delete(id); - return this.spawn(sources, { unique: true }).filter(selector); - }; -} + var unmergedLastEle = i === this.length - 1; -util.extend(elesfn, { - edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'), + // replace empty spot with last ele in collection + if (this.length > 1 && !unmergedLastEle) { + var lastEleI = this.length - 1; + var lastEle = this[lastEleI]; + var lastEleId = lastEle._private.data.id; - edgesTo: cache(defineEdgesWithFunction({ - thisIsSrc: true - }), 'edgesTo') -}); + this[lastEleI] = undefined; + this[i] = lastEle; + map.set(lastEleId, { ele: lastEle, index: i }); + } -function defineEdgesWithFunction(params) { + // the collection is now 1 ele smaller + this.length--; - return function edgesWithImpl(otherNodes) { - var elements = []; + return this; + }, + + // remove eles in place on calling collection + unmerge: function unmerge(toRemove) { var cy = this._private.cy; - var p = params || {}; - // get elements if a selector is specified - if (is.string(otherNodes)) { - otherNodes = cy.$(otherNodes); + if (!toRemove) { + return this; } - for (var h = 0; h < otherNodes.length; h++) { - var edges = otherNodes[h]._private.edges; + if (toRemove && is.string(toRemove)) { + var selector = toRemove; + toRemove = cy.mutableElements().filter(selector); + } - for (var i = 0; i < edges.length; i++) { - var edge = edges[i]; - var edgeData = edge._private.data; - var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target); - var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target); - var edgeConnectsThisAndOther = thisToOther || otherToThis; + for (var i = 0; i < toRemove.length; i++) { + this.unmergeOne(toRemove[i]); + } - if (!edgeConnectsThisAndOther) { - continue; - } + return this; // chaining + }, - if (p.thisIsSrc || p.thisIsTgt) { - if (p.thisIsSrc && !thisToOther) { - continue; - } + map: function map(mapFn, thisArg) { + var arr = []; + var eles = this; - if (p.thisIsTgt && !otherToThis) { - continue; - } - } + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles); - elements.push(edge); - } + arr.push(ret); } - return this.spawn(elements, { unique: true }); - }; -} - -util.extend(elesfn, { - connectedEdges: cache(function (selector) { - var retEles = []; + return arr; + }, + reduce: function reduce(fn, initialValue) { + var val = initialValue; var eles = this; + for (var i = 0; i < eles.length; i++) { - var node = eles[i]; - if (!node.isNode()) { - continue; - } + val = fn(val, eles[i], i, eles); + } - var edges = node._private.edges; + return val; + }, - for (var j = 0; j < edges.length; j++) { - var edge = edges[j]; - retEles.push(edge); + max: function max(valFn, thisArg) { + var max = -Infinity; + var maxEle = void 0; + var eles = this; + + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles); + + if (val > max) { + max = val; + maxEle = ele; } } - return this.spawn(retEles, { unique: true }).filter(selector); - }, 'connectedEdges'), - - connectedNodes: cache(function (selector) { - var retEles = []; + return { + value: max, + ele: maxEle + }; + }, + min: function min(valFn, thisArg) { + var min = Infinity; + var minEle = void 0; var eles = this; + for (var i = 0; i < eles.length; i++) { - var edge = eles[i]; - if (!edge.isEdge()) { - continue; - } + var ele = eles[i]; + var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles); - retEles.push(edge.source()[0]); - retEles.push(edge.target()[0]); + if (val < min) { + min = val; + minEle = ele; + } } - return this.spawn(retEles, { unique: true }).filter(selector); - }, 'connectedNodes'), + return { + value: min, + ele: minEle + }; + } +}; - parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'), +// aliases +var fn = elesfn; +fn['u'] = fn['|'] = fn['+'] = fn.union = fn.or = fn.add; +fn['\\'] = fn['!'] = fn['-'] = fn.difference = fn.relativeComplement = fn.subtract = fn.not; +fn['n'] = fn['&'] = fn['.'] = fn.and = fn.intersection = fn.intersect; +fn['^'] = fn['(+)'] = fn['(-)'] = fn.symmetricDifference = fn.symdiff = fn.xor; +fn.fnFilter = fn.filterFn = fn.stdFilter = fn.filter; +fn.complement = fn.abscomp = fn.absoluteComplement; - codirectedEdges: cache(defineParallelEdgesFunction({ - codirected: true - }), 'codirectedEdges') -}); +module.exports = elesfn; -function defineParallelEdgesFunction(params) { - var defaults = { - codirected: false - }; - params = util.extend({}, defaults, params); +/***/ }), +/* 63 */ +/***/ (function(module, exports, __webpack_require__) { - return function parallelEdgesImpl(selector) { - // micro-optimised for renderer - var elements = []; - var edges = this.edges(); - var p = params; +"use strict"; - // look at all the edges in the collection - for (var i = 0; i < edges.length; i++) { - var edge1 = edges[i]; - var edge1_p = edge1._private; - var src1 = edge1_p.source; - var srcid1 = src1._private.data.id; - var tgtid1 = edge1_p.data.target; - var srcEdges1 = src1._private.edges; - // look at edges connected to the src node of this edge - for (var j = 0; j < srcEdges1.length; j++) { - var edge2 = srcEdges1[j]; - var edge2data = edge2._private.data; - var tgtid2 = edge2data.target; - var srcid2 = edge2data.source; +var elesfn = { + isNode: function isNode() { + return this.group() === 'nodes'; + }, - var codirected = tgtid2 === tgtid1 && srcid2 === srcid1; - var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2; + isEdge: function isEdge() { + return this.group() === 'edges'; + }, - if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) { - elements.push(edge2); - } - } + isLoop: function isLoop() { + return this.isEdge() && this.source().id() === this.target().id(); + }, + + isSimple: function isSimple() { + return this.isEdge() && this.source().id() !== this.target().id(); + }, + + group: function group() { + var ele = this[0]; + + if (ele) { + return ele._private.group; } + } +}; - return this.spawn(elements, { unique: true }).filter(selector); - }; -} +module.exports = elesfn; -// Misc functions -///////////////// +/***/ }), +/* 64 */ +/***/ (function(module, exports, __webpack_require__) { -util.extend(elesfn, { - components: function components() { - var self = this; - var cy = self.cy(); - var visited = self.spawn(); - var unvisited = self.nodes().spawnSelf(); - var components = []; +"use strict"; - var visitInComponent = function visitInComponent(node, component) { - visited.merge(node); - unvisited.unmerge(node); - component.merge(node); - }; - if (unvisited.empty()) { - return self.spawn(); - } +var is = __webpack_require__(0); +var zIndexSort = __webpack_require__(17); +var util = __webpack_require__(1); - var _loop = function _loop() { - var component = cy.collection(); - components.push(component); +var elesfn = { + forEach: function forEach(fn, thisArg) { + if (is.fn(fn)) { - var root = unvisited[0]; - visitInComponent(root, component); + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + var ret = thisArg ? fn.apply(thisArg, [ele, i, this]) : fn(ele, i, this); - self.bfs({ - directed: false, - roots: root, - visit: function visit(v, e, u, i, depth) { - visitInComponent(v, component); - } - }); - }; + if (ret === false) { + break; + } // exit each early on return false + } + } - do { - _loop(); - } while (unvisited.length > 0); + return this; + }, - return components.map(function (component) { - var connectedEdges = component.connectedEdges().stdFilter(function (edge) { - return component.anySame(edge.source()) && component.anySame(edge.target()); - }); + toArray: function toArray() { + var array = []; - return component.union(connectedEdges); - }); - } -}); + for (var i = 0; i < this.length; i++) { + array.push(this[i]); + } -module.exports = elesfn; + return array; + }, -/***/ }), -/* 55 */ -/***/ (function(module, exports, __webpack_require__) { + slice: function slice(start, end) { + var array = []; + var thisSize = this.length; -"use strict"; + if (end == null) { + end = thisSize; + } + if (start == null) { + start = 0; + } -var is = __webpack_require__(0); -var util = __webpack_require__(1); -var Collection = __webpack_require__(7); -var Element = __webpack_require__(13); + if (start < 0) { + start = thisSize + start; + } -var corefn = { - add: function add(opts) { + if (end < 0) { + end = thisSize + end; + } - var elements = void 0; - var cy = this; + for (var i = start; i >= 0 && i < end && i < thisSize; i++) { + array.push(this[i]); + } - // add the elements - if (is.elementOrCollection(opts)) { - var eles = opts; + return this.spawn(array); + }, - if (eles._private.cy === cy) { - // same instance => just restore - elements = eles.restore(); - } else { - // otherwise, copy from json - var jsons = []; + size: function size() { + return this.length; + }, - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - jsons.push(ele.json()); - } + eq: function eq(i) { + return this[i] || this.spawn(); + }, - elements = new Collection(cy, jsons); - } - } + first: function first() { + return this[0] || this.spawn(); + }, - // specify an array of options - else if (is.array(opts)) { - var _jsons = opts; + last: function last() { + return this[this.length - 1] || this.spawn(); + }, - elements = new Collection(cy, _jsons); - } + empty: function empty() { + return this.length === 0; + }, - // specify via opts.nodes and opts.edges - else if (is.plainObject(opts) && (is.array(opts.nodes) || is.array(opts.edges))) { - var elesByGroup = opts; - var _jsons2 = []; + nonempty: function nonempty() { + return !this.empty(); + }, - var grs = ['nodes', 'edges']; - for (var _i = 0, il = grs.length; _i < il; _i++) { - var group = grs[_i]; - var elesArray = elesByGroup[group]; + sort: function sort(sortFn) { + if (!is.fn(sortFn)) { + return this; + } - if (is.array(elesArray)) { + var sorted = this.toArray().sort(sortFn); - for (var j = 0, jl = elesArray.length; j < jl; j++) { - var json = util.extend({ group: group }, elesArray[j]); + return this.spawn(sorted); + }, - _jsons2.push(json); - } - } - } + sortByZIndex: function sortByZIndex() { + return this.sort(zIndexSort); + }, - elements = new Collection(cy, _jsons2); - } + zDepth: function zDepth() { + var ele = this[0]; + if (!ele) { + return undefined; + } - // specify options for one element - else { - var _json = opts; - elements = new Element(cy, _json).collection(); - } + // let cy = ele.cy(); + var _p = ele._private; + var group = _p.group; - return elements; - }, + if (group === 'nodes') { + var depth = _p.data.parent ? ele.parents().size() : 0; - remove: function remove(collection) { - if (is.elementOrCollection(collection)) { - // already have right ref - } else if (is.string(collection)) { - var selector = collection; - collection = this.$(selector); - } + if (!ele.isParent()) { + return util.MAX_INT - 1; // childless nodes always on top + } - return collection.remove(); + return depth; + } else { + var src = _p.source; + var tgt = _p.target; + var srcDepth = src.zDepth(); + var tgtDepth = tgt.zDepth(); + + return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent + } } }; -module.exports = corefn; +elesfn.each = elesfn.forEach; + +module.exports = elesfn; /***/ }), -/* 56 */ +/* 65 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* global Float32Array */ - -/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */ -function generateCubicBezier(mX1, mY1, mX2, mY2) { - var NEWTON_ITERATIONS = 4, - NEWTON_MIN_SLOPE = 0.001, - SUBDIVISION_PRECISION = 0.0000001, - SUBDIVISION_MAX_ITERATIONS = 10, - kSplineTableSize = 11, - kSampleStepSize = 1.0 / (kSplineTableSize - 1.0), - float32ArraySupported = typeof Float32Array !== 'undefined'; +var is = __webpack_require__(0); +var util = __webpack_require__(1); +var Promise = __webpack_require__(5); +var math = __webpack_require__(2); - /* Must contain four arguments. */ - if (arguments.length !== 4) { - return false; - } +var elesfn = { + // Calculates and returns node dimensions { x, y } based on options given + layoutDimensions: function layoutDimensions(options) { + options = util.assign({ + nodeDimensionsIncludeLabels: true + }, options); - /* Arguments must be numbers. */ - for (var i = 0; i < 4; ++i) { - if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) { - return false; + if (options.nodeDimensionsIncludeLabels) { + var bbDim = this.boundingBox(); + return { + w: bbDim.w, + h: bbDim.h + }; + } else { + return { + w: this.outerWidth(), + h: this.outerHeight() + }; } - } - - /* X values must be in the [0, 1] range. */ - mX1 = Math.min(mX1, 1); - mX2 = Math.min(mX2, 1); - mX1 = Math.max(mX1, 0); - mX2 = Math.max(mX2, 0); + }, - var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); + // using standard layout options, apply position function (w/ or w/o animation) + layoutPositions: function layoutPositions(layout, options, fn) { + var nodes = this.nodes(); + var cy = this.cy(); + var layoutEles = options.eles; // nodes & edges + var getMemoizeKey = function getMemoizeKey(node, i) { + return node.id() + '$' + i; + }; + var fnMem = util.memoize(fn, getMemoizeKey); // memoized version of position function - function A(aA1, aA2) { - return 1.0 - 3.0 * aA2 + 3.0 * aA1; - } + layout.emit({ type: 'layoutstart', layout: layout }); - function B(aA1, aA2) { - return 3.0 * aA2 - 6.0 * aA1; - } + layout.animations = []; - function C(aA1) { - return 3.0 * aA1; - } + var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) { + var center = { + x: nodesBb.x1 + nodesBb.w / 2, + y: nodesBb.y1 + nodesBb.h / 2 + }; - function calcBezier(aT, aA1, aA2) { - return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; - } + var spacingVector = { // scale from center of bounding box (not necessarily 0,0) + x: (pos.x - center.x) * spacing, + y: (pos.y - center.y) * spacing + }; - function getSlope(aT, aA1, aA2) { - return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); - } + return { + x: center.x + spacingVector.x, + y: center.y + spacingVector.y + }; + }; - function newtonRaphsonIterate(aX, aGuessT) { - for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) { - var currentSlope = getSlope(aGuessT, mX1, mX2); + var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1; - if (currentSlope === 0.0) { - return aGuessT; + var spacingBb = function spacingBb() { + if (!useSpacingFactor) { + return null; } - var currentX = calcBezier(aGuessT, mX1, mX2) - aX; - aGuessT -= currentX / currentSlope; - } + var bb = math.makeBoundingBox(); - return aGuessT; - } + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var pos = fnMem(node, i); - function calcSampleValues() { - for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) { - mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2); - } - } + math.expandBoundingBoxByPoint(bb, pos.x, pos.y); + } - function binarySubdivide(aX, aA, aB) { - var currentX = void 0, - currentT = void 0, - i = 0; + return bb; + }; - do { - currentT = aA + (aB - aA) / 2.0; - currentX = calcBezier(currentT, mX1, mX2) - aX; - if (currentX > 0.0) { - aB = currentT; - } else { - aA = currentT; + var bb = spacingBb(); + + var getFinalPos = util.memoize(function (node, i) { + var newPos = fnMem(node, i); + var pos = node.position(); + + if (!is.number(pos.x) || !is.number(pos.y)) { + node.silentPosition({ x: 0, y: 0 }); } - } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); - return currentT; - } + if (useSpacingFactor) { + var spacing = Math.abs(options.spacingFactor); - function getTForX(aX) { - var intervalStart = 0.0, - currentSample = 1, - lastSample = kSplineTableSize - 1; + newPos = calculateSpacing(spacing, bb, newPos); + } - for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) { - intervalStart += kSampleStepSize; - } + if (options.transform != null) { + newPos = options.transform(node, newPos); + } - --currentSample; + return newPos; + }, getMemoizeKey); - var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]), - guessForT = intervalStart + dist * kSampleStepSize, - initialSlope = getSlope(guessForT, mX1, mX2); + if (options.animate) { + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var newPos = getFinalPos(node, i); + var animateNode = options.animateFilter == null || options.animateFilter(node, i); - if (initialSlope >= NEWTON_MIN_SLOPE) { - return newtonRaphsonIterate(aX, guessForT); - } else if (initialSlope === 0.0) { - return guessForT; + if (animateNode) { + var ani = node.animation({ + position: newPos, + duration: options.animationDuration, + easing: options.animationEasing + }); + + layout.animations.push(ani); + + ani.play(); + } else { + node.position(newPos); + } + } + + if (options.fit) { + var fitAni = cy.animation({ + fit: { + boundingBox: layoutEles.boundingBoxAt(getFinalPos), + padding: options.padding + }, + duration: options.animationDuration, + easing: options.animationEasing + }); + + layout.animations.push(fitAni); + + fitAni.play(); + } else if (options.zoom !== undefined && options.pan !== undefined) { + var zoomPanAni = cy.animation({ + zoom: options.zoom, + pan: options.pan, + duration: options.animationDuration, + easing: options.animationEasing + }); + + layout.animations.push(zoomPanAni); + + zoomPanAni.play(); + } + + layout.one('layoutready', options.ready); + layout.emit({ type: 'layoutready', layout: layout }); + + Promise.all(layout.animations.map(function (ani) { + return ani.promise(); + })).then(function () { + layout.one('layoutstop', options.stop); + layout.emit({ type: 'layoutstop', layout: layout }); + }); } else { - return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize); - } - } - var _precomputed = false; + nodes.positions(getFinalPos); - function precompute() { - _precomputed = true; - if (mX1 !== mY1 || mX2 !== mY2) { - calcSampleValues(); - } - } + if (options.fit) { + cy.fit(options.eles, options.padding); + } - var f = function f(aX) { - if (!_precomputed) { - precompute(); - } - if (mX1 === mY1 && mX2 === mY2) { - return aX; - } - if (aX === 0) { - return 0; - } - if (aX === 1) { - return 1; + if (options.zoom != null) { + cy.zoom(options.zoom); + } + + if (options.pan) { + cy.pan(options.pan); + } + + layout.one('layoutready', options.ready); + layout.emit({ type: 'layoutready', layout: layout }); + + layout.one('layoutstop', options.stop); + layout.emit({ type: 'layoutstop', layout: layout }); } - return calcBezier(getTForX(aX), mY1, mY2); - }; + return this; // chaining + }, - f.getControlPoints = function () { - return [{ - x: mX1, - y: mY1 - }, { - x: mX2, - y: mY2 - }]; - }; + layout: function layout(options) { + var cy = this.cy(); - var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")"; - f.toString = function () { - return str; - }; + return cy.makeLayout(util.extend({}, options, { + eles: this + })); + } - return f; -} +}; -module.exports = generateCubicBezier; +// aliases: +elesfn.createLayout = elesfn.makeLayout = elesfn.layout; + +module.exports = elesfn; /***/ }), -/* 57 */ +/* 66 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -11061,1103 +11334,1090 @@ module.exports = generateCubicBezier; var is = __webpack_require__(0); -function getEasedValue(type, start, end, percent, easingFn) { - if (percent === 1) { - return end; - } - - var val = easingFn(start, end, percent); +function styleCache(key, fn, ele) { + var _p = ele._private; + var cache = _p.styleCache = _p.styleCache || {}; + var val; - if (type == null) { + if ((val = cache[key]) != null) { return val; - } - - if (type.roundValue || type.color) { - val = Math.round(val); - } - - if (type.min !== undefined) { - val = Math.max(val, type.min); - } + } else { + val = cache[key] = fn(ele); - if (type.max !== undefined) { - val = Math.min(val, type.max); + return val; } +} - return val; +function cacheStyleFunction(key, fn) { + return function cachedStyleFunction(ele) { + return styleCache(key, fn, ele); + }; } -function ease(startProp, endProp, percent, easingFn, propSpec) { - var type = propSpec != null ? propSpec.type : null; - - if (percent < 0) { - percent = 0; - } else if (percent > 1) { - percent = 1; - } - - var start = void 0, - end = void 0; - - if (startProp.pfValue != null || startProp.value != null) { - start = startProp.pfValue != null ? startProp.pfValue : startProp.value; - } else { - start = startProp; - } +function cachePrototypeStyleFunction(key, fn) { + var selfFn = function selfFn(ele) { + return fn.call(ele); + }; - if (endProp.pfValue != null || endProp.value != null) { - end = endProp.pfValue != null ? endProp.pfValue : endProp.value; - } else { - end = endProp; - } + return function cachedPrototypeStyleFunction() { + var ele = this[0]; - if (is.number(start) && is.number(end)) { - return getEasedValue(type, start, end, percent, easingFn); - } else if (is.array(start) && is.array(end)) { - var easedArr = []; + if (ele) { + return styleCache(key, selfFn, ele); + } + }; +} - for (var i = 0; i < end.length; i++) { - var si = start[i]; - var ei = end[i]; +var elesfn = { - if (si != null && ei != null) { - var val = getEasedValue(type, si, ei, percent, easingFn); + recalculateRenderedStyle: function recalculateRenderedStyle(useCache) { + var cy = this.cy(); + var renderer = cy.renderer(); + var styleEnabled = cy.styleEnabled(); - easedArr.push(val); - } else { - easedArr.push(ei); - } + if (renderer && styleEnabled) { + renderer.recalculateRenderedStyle(this, useCache); } - return easedArr; - } - - return undefined; -} - -module.exports = ease; + return this; + }, -/***/ }), -/* 58 */ -/***/ (function(module, exports, __webpack_require__) { + dirtyStyleCache: function dirtyStyleCache() { + var cy = this.cy(); + var dirty = function dirty(ele) { + return ele._private.styleCache = {}; + }; -"use strict"; + if (cy.hasCompoundNodes()) { + var eles = void 0; + eles = this.spawnSelf().merge(this.descendants()).merge(this.parents()); -var generateCubicBezier = __webpack_require__(56); -var generateSpringRK4 = __webpack_require__(60); + eles.merge(eles.connectedEdges()); -var cubicBezier = function cubicBezier(t1, p1, t2, p2) { - var bezier = generateCubicBezier(t1, p1, t2, p2); + eles.forEach(dirty); + } else { + this.forEach(function (ele) { + dirty(ele); - return function (start, end, percent) { - return start + (end - start) * bezier(percent); - }; -}; + ele.connectedEdges().forEach(dirty); + }); + } -var easings = { - 'linear': function linear(start, end, percent) { - return start + (end - start) * percent; + return this; }, - // default easings - 'ease': cubicBezier(0.25, 0.1, 0.25, 1), - 'ease-in': cubicBezier(0.42, 0, 1, 1), - 'ease-out': cubicBezier(0, 0, 0.58, 1), - 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1), - - // sine - 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715), - 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1), - 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95), - - // quad - 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53), - 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94), - 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955), + // fully updates (recalculates) the style for the elements + updateStyle: function updateStyle(notifyRenderer) { + var cy = this._private.cy; - // cubic - 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19), - 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1), - 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1), + if (!cy.styleEnabled()) { + return this; + } - // quart - 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22), - 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1), - 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1), + if (cy._private.batchingStyle) { + var bEles = cy._private.batchStyleEles; - // quint - 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06), - 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1), - 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1), + bEles.merge(this); - // expo - 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035), - 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1), - 'ease-in-out-expo': cubicBezier(1, 0, 0, 1), + return this; // chaining and exit early when batching + } - // circ - 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335), - 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1), - 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86), + var hasCompounds = cy.hasCompoundNodes(); + var style = cy.style(); + var updatedEles = this; - // user param easings... + notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false; - 'spring': function spring(tension, friction, duration) { - if (duration === 0) { - // can't get a spring w/ duration 0 - return easings.linear; // duration 0 => jump to end so impl doesn't matter + if (hasCompounds) { + // then add everything up and down for compound selector checks + updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents()); } - var spring = generateSpringRK4(tension, friction, duration); - - return function (start, end, percent) { - return start + (end - start) * spring(percent); - }; - }, + var changedEles = style.apply(updatedEles); - 'cubic-bezier': cubicBezier -}; + changedEles.dirtyStyleCache(); + changedEles.dirtyCompoundBoundsCache(); -module.exports = easings; + if (notifyRenderer) { + changedEles.emitAndNotify('style'); // let renderer know we changed style + } else { + changedEles.emit('style'); // just fire the event + } -/***/ }), -/* 59 */ -/***/ (function(module, exports, __webpack_require__) { + return this; // chaining + }, -"use strict"; + // just update the mappers in the elements' styles; cheaper than eles.updateStyle() + updateMappers: function updateMappers(notifyRenderer) { + var cy = this._private.cy; + var style = cy.style(); + notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false; + if (!cy.styleEnabled()) { + return this; + } -var define = __webpack_require__(3); -var util = __webpack_require__(1); -var stepAll = __webpack_require__(62); + var changedEles = style.updateMappers(this); -var corefn = { + changedEles.dirtyStyleCache(); + changedEles.dirtyCompoundBoundsCache(); - // pull in animation functions - animate: define.animate(), - animation: define.animation(), - animated: define.animated(), - clearQueue: define.clearQueue(), - delay: define.delay(), - delayAnimation: define.delayAnimation(), - stop: define.stop(), + if (notifyRenderer) { + changedEles.emitAndNotify('style'); // let renderer know we changed style + } else { + changedEles.emit('style'); // just fire the event + } + return this; // chaining + }, - addToAnimationPool: function addToAnimationPool(eles) { - var cy = this; + // get the internal parsed style object for the specified property + parsedStyle: function parsedStyle(property) { + var ele = this[0]; + var cy = ele.cy(); if (!cy.styleEnabled()) { return; - } // save cycles when no style used - - cy._private.aniEles.merge(eles); - }, + } - stopAnimationLoop: function stopAnimationLoop() { - this._private.animationsRunning = false; + if (ele) { + return ele._private.style[property] || cy.style().getDefaultProperty(property); + } }, - startAnimationLoop: function startAnimationLoop() { - var cy = this; - - cy._private.animationsRunning = true; + numericStyle: function numericStyle(property) { + var ele = this[0]; - if (!cy.styleEnabled()) { + if (!ele.cy().styleEnabled()) { return; - } // save cycles when no style used - - // NB the animation loop will exec in headless environments if style enabled - // and explicit cy.destroy() is necessary to stop the loop + } - function headlessStep() { - if (!cy._private.animationsRunning) { - return; - } + if (ele) { + var pstyle = ele.pstyle(property); - util.requestAnimationFrame(function animationStep(now) { - stepAll(now, cy); - headlessStep(); - }); + return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value; } + }, - var renderer = cy.renderer(); + numericStyleUnits: function numericStyleUnits(property) { + var ele = this[0]; - if (renderer && renderer.beforeRender) { - // let the renderer schedule animations - renderer.beforeRender(function rendererAnimationStep(willDraw, now) { - stepAll(now, cy); - }, renderer.beforeRenderPriorities.animations); - } else { - // manage the animation loop ourselves - headlessStep(); // first call + if (!ele.cy().styleEnabled()) { + return; } - } -}; - -module.exports = corefn; - -/***/ }), -/* 60 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; + if (ele) { + return ele.pstyle(property).units; + } + }, + // get the specified css property as a rendered value (i.e. on-screen value) + // or get the whole rendered style if no property specified (NB doesn't allow setting) + renderedStyle: function renderedStyle(property) { + var cy = this.cy(); + if (!cy.styleEnabled()) { + return this; + } -/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */ -/* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass - then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */ -var generateSpringRK4 = function () { - function springAccelerationForState(state) { - return -state.tension * state.x - state.friction * state.v; - } + var ele = this[0]; - function springEvaluateStateWithDerivative(initialState, dt, derivative) { - var state = { - x: initialState.x + derivative.dx * dt, - v: initialState.v + derivative.dv * dt, - tension: initialState.tension, - friction: initialState.friction - }; + if (ele) { + return cy.style().getRenderedStyle(ele, property); + } + }, - return { dx: state.v, dv: springAccelerationForState(state) }; - } + // read the calculated css style of the element or override the style (via a bypass) + style: function style(name, value) { + var cy = this.cy(); - function springIntegrateState(state, dt) { - var a = { - dx: state.v, - dv: springAccelerationForState(state) - }, - b = springEvaluateStateWithDerivative(state, dt * 0.5, a), - c = springEvaluateStateWithDerivative(state, dt * 0.5, b), - d = springEvaluateStateWithDerivative(state, dt, c), - dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx), - dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv); + if (!cy.styleEnabled()) { + return this; + } - state.x = state.x + dxdt * dt; - state.v = state.v + dvdt * dt; + var updateTransitions = false; + var style = cy.style(); - return state; - } + if (is.plainObject(name)) { + // then extend the bypass + var props = name; + style.applyBypass(this, props, updateTransitions); - return function springRK4Factory(tension, friction, duration) { + this.dirtyStyleCache(); + this.dirtyCompoundBoundsCache(); - var initState = { - x: -1, - v: 0, - tension: null, - friction: null - }, - path = [0], - time_lapsed = 0, - tolerance = 1 / 10000, - DT = 16 / 1000, - have_duration = void 0, - dt = void 0, - last_state = void 0; + this.emitAndNotify('style'); // let the renderer know we've updated style + } else if (is.string(name)) { - tension = parseFloat(tension) || 500; - friction = parseFloat(friction) || 20; - duration = duration || null; + if (value === undefined) { + // then get the property from the style + var ele = this[0]; - initState.tension = tension; - initState.friction = friction; + if (ele) { + return style.getStylePropertyValue(ele, name); + } else { + // empty collection => can't get any value + return; + } + } else { + // then set the bypass with the property value + style.applyBypass(this, name, value, updateTransitions); - have_duration = duration !== null; + this.dirtyStyleCache(); + this.dirtyCompoundBoundsCache(); - /* Calculate the actual time it takes for this animation to complete with the provided conditions. */ - if (have_duration) { - /* Run the simulation without a duration. */ - time_lapsed = springRK4Factory(tension, friction); - /* Compute the adjusted time delta. */ - dt = time_lapsed / duration * DT; - } else { - dt = DT; - } + this.emitAndNotify('style'); // let the renderer know we've updated style + } + } else if (name === undefined) { + var _ele = this[0]; - for (;;) { - /* Next/step function .*/ - last_state = springIntegrateState(last_state || initState, dt); - /* Store the position. */ - path.push(1 + last_state.x); - time_lapsed += 16; - /* If the change threshold is reached, break. */ - if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) { - break; + if (_ele) { + return style.getRawStyle(_ele); + } else { + // empty collection => can't get any value + return; } } - /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the - computed path and returns a snapshot of the position according to a given percentComplete. */ - return !have_duration ? time_lapsed : function (percentComplete) { - return path[percentComplete * (path.length - 1) | 0]; - }; - }; -}(); - -module.exports = generateSpringRK4; - -/***/ }), -/* 61 */ -/***/ (function(module, exports, __webpack_require__) { + return this; // chaining + }, -"use strict"; + removeStyle: function removeStyle(names) { + var cy = this.cy(); + if (!cy.styleEnabled()) { + return this; + } -function startAnimation(self, ani, now, isCore) { - var isEles = !isCore; - var ele = self; - var ani_p = ani._private; - var cy = isCore ? self : self.cy(); - var style = cy.style(); + var updateTransitions = false; + var style = cy.style(); + var eles = this; - if (isEles) { - var pos = ele.position(); + if (names === undefined) { + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - ani_p.startPosition = ani_p.startPosition || { - x: pos.x, - y: pos.y - }; + style.removeAllBypasses(ele, updateTransitions); + } + } else { + names = names.split(/\s+/); - ani_p.startStyle = ani_p.startStyle || style.getAnimationStartStyle(ele, ani_p.style); - } + for (var _i = 0; _i < eles.length; _i++) { + var _ele2 = eles[_i]; - if (isCore) { - var pan = cy._private.pan; + style.removeBypasses(_ele2, names, updateTransitions); + } + } - ani_p.startPan = ani_p.startPan || { - x: pan.x, - y: pan.y - }; + this.dirtyStyleCache(); + this.dirtyCompoundBoundsCache(); - ani_p.startZoom = ani_p.startZoom != null ? ani_p.startZoom : cy._private.zoom; - } + this.emitAndNotify('style'); // let the renderer know we've updated style - ani_p.started = true; - ani_p.startTime = now - ani_p.progress * ani_p.duration; -} + return this; // chaining + }, -module.exports = startAnimation; + show: function show() { + this.css('display', 'element'); + return this; // chaining + }, -/***/ }), -/* 62 */ -/***/ (function(module, exports, __webpack_require__) { + hide: function hide() { + this.css('display', 'none'); + return this; // chaining + }, -"use strict"; + effectiveOpacity: function effectiveOpacity() { + var cy = this.cy(); + if (!cy.styleEnabled()) { + return 1; + } + var hasCompoundNodes = cy.hasCompoundNodes(); + var ele = this[0]; -var step = __webpack_require__(63); -var startAnimation = __webpack_require__(61); + if (ele) { + var _p = ele._private; + var parentOpacity = ele.pstyle('opacity').value; -function stepAll(now, cy) { - var eles = cy._private.aniEles; - var doneEles = []; + if (!hasCompoundNodes) { + return parentOpacity; + } - function stepOne(ele, isCore) { - var _p = ele._private; - var current = _p.animation.current; - var queue = _p.animation.queue; - var ranAnis = false; + var parents = !_p.data.parent ? null : ele.parents(); - // cancel all animations on display:none ele - if (!isCore && ele.pstyle('display').value === 'none') { - // put all current and queue animations in this tick's current list - // and empty the lists for the element - current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); + if (parents) { + for (var i = 0; i < parents.length; i++) { + var parent = parents[i]; + var opacity = parent.pstyle('opacity').value; - // stop all animations - for (var i = 0; i < current.length; i++) { - current[i].stop(); + parentOpacity = opacity * parentOpacity; + } } - } - - // if nothing currently animating, get something from the queue - if (current.length === 0) { - var next = queue.shift(); - if (next) { - current.push(next); - } + return parentOpacity; } + }, - var callbacks = function callbacks(_callbacks) { - for (var j = _callbacks.length - 1; j >= 0; j--) { - var cb = _callbacks[j]; + transparent: function transparent() { + var cy = this.cy(); + if (!cy.styleEnabled()) { + return false; + } - cb(); + var ele = this[0]; + var hasCompoundNodes = ele.cy().hasCompoundNodes(); + + if (ele) { + if (!hasCompoundNodes) { + return ele.pstyle('opacity').value === 0; + } else { + return ele.effectiveOpacity() === 0; } + } + }, - _callbacks.splice(0, _callbacks.length); - }; + backgrounding: function backgrounding() { + var cy = this.cy(); + if (!cy.styleEnabled()) { + return false; + } - // step and remove if done - for (var _i = current.length - 1; _i >= 0; _i--) { - var ani = current[_i]; - var ani_p = ani._private; + var ele = this[0]; - if (ani_p.stopped) { - current.splice(_i, 1); + return ele._private.backgrounding ? true : false; + } - ani_p.hooked = false; - ani_p.playing = false; - ani_p.started = false; +}; - callbacks(ani_p.frames); +function checkCompound(ele, parentOk) { + var _p = ele._private; + var parents = _p.data.parent ? ele.parents() : null; - continue; - } + if (parents) { + for (var i = 0; i < parents.length; i++) { + var parent = parents[i]; - if (!ani_p.playing && !ani_p.applying) { - continue; + if (!parentOk(parent)) { + return false; } + } + } - // an apply() while playing shouldn't do anything - if (ani_p.playing && ani_p.applying) { - ani_p.applying = false; - } + return true; +} - if (!ani_p.started) { - startAnimation(ele, ani, now, isCore); - } +function defineDerivedStateFunction(specs) { + var ok = specs.ok; + var edgeOkViaNode = specs.edgeOkViaNode || specs.ok; + var parentOk = specs.parentOk || specs.ok; - step(ele, ani, now, isCore); + return function () { + var cy = this.cy(); + if (!cy.styleEnabled()) { + return true; + } - if (ani_p.applying) { - ani_p.applying = false; - } + var ele = this[0]; + var hasCompoundNodes = cy.hasCompoundNodes(); - callbacks(ani_p.frames); + if (ele) { + var _p = ele._private; - if (ani.completed()) { - current.splice(_i, 1); + if (!ok(ele)) { + return false; + } - ani_p.hooked = false; - ani_p.playing = false; - ani_p.started = false; + if (ele.isNode()) { + return !hasCompoundNodes || checkCompound(ele, parentOk); + } else { + var src = _p.source; + var tgt = _p.target; - callbacks(ani_p.completes); + return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode))); } - - ranAnis = true; } + }; +} - if (!isCore && current.length === 0 && queue.length === 0) { - doneEles.push(ele); - } +var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) { + return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true); +}); - return ranAnis; - } // stepElement +elesfn.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({ + ok: eleTakesUpSpace +})); - // handle all eles - var ranEleAni = false; - for (var e = 0; e < eles.length; e++) { - var ele = eles[e]; - var handledThisEle = stepOne(ele); +var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) { + return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele); +}); - ranEleAni = ranEleAni || handledThisEle; - } // each element +var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) { + return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent); +}); - var ranCoreAni = stepOne(cy, true); +elesfn.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({ + ok: eleInteractive, + parentOk: parentInteractive, + edgeOkViaNode: eleTakesUpSpace +})); - // notify renderer - if (ranEleAni || ranCoreAni) { - if (eles.length > 0) { - eles.dirtyCompoundBoundsCache(); +elesfn.noninteractive = function () { + var ele = this[0]; - cy.notify({ - type: 'draw', - eles: eles - }); - } else { - cy.notify({ - type: 'draw' - }); - } + if (ele) { + return !ele.interactive(); } +}; - // remove elements from list of currently animating if its queues are empty - eles.unmerge(doneEles); +var eleVisible = cacheStyleFunction('eleVisible', function (ele) { + return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele); +}); - cy.emit('step'); -} // stepAll +var edgeVisibleViaNode = eleTakesUpSpace; -module.exports = stepAll; +elesfn.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({ + ok: eleVisible, + edgeOkViaNode: edgeVisibleViaNode +})); -/***/ }), -/* 63 */ -/***/ (function(module, exports, __webpack_require__) { +elesfn.hidden = function () { + var ele = this[0]; -"use strict"; + if (ele) { + return !ele.visible(); + } +}; +elesfn.bypass = elesfn.css = elesfn.style; +elesfn.renderedCss = elesfn.renderedStyle; +elesfn.removeBypass = elesfn.removeCss = elesfn.removeStyle; +elesfn.pstyle = elesfn.parsedStyle; -var easings = __webpack_require__(58); -var ease = __webpack_require__(57); -var is = __webpack_require__(0); +module.exports = elesfn; -function step(self, ani, now, isCore) { - var isEles = !isCore; - var _p = self._private; - var ani_p = ani._private; - var pEasing = ani_p.easing; - var startTime = ani_p.startTime; - var cy = isCore ? self : self.cy(); - var style = cy.style(); +/***/ }), +/* 67 */ +/***/ (function(module, exports, __webpack_require__) { - if (!ani_p.easingImpl) { +"use strict"; - if (pEasing == null) { - // use default - ani_p.easingImpl = easings['linear']; - } else { - // then define w/ name - var easingVals = void 0; - if (is.string(pEasing)) { - var easingProp = style.parse('transition-timing-function', pEasing); +var elesfn = {}; - easingVals = easingProp.value; - } else { - // then assume preparsed array - easingVals = pEasing; - } +function defineSwitchFunction(params) { + return function () { + var args = arguments; + var changedEles = []; - var name = void 0, - args = void 0; + // e.g. cy.nodes().select( data, handler ) + if (args.length === 2) { + var data = args[0]; + var handler = args[1]; + this.on(params.event, data, handler); + } - if (is.string(easingVals)) { - name = easingVals; - args = []; - } else { - name = easingVals[1]; - args = easingVals.slice(2).map(function (n) { - return +n; - }); + // e.g. cy.nodes().select( handler ) + else if (args.length === 1) { + var _handler = args[0]; + this.on(params.event, _handler); } - if (args.length > 0) { - // create with args - if (name === 'spring') { - args.push(ani_p.duration); // need duration to generate spring - } + // e.g. cy.nodes().select() + else if (args.length === 0) { + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + var able = !params.ableField || ele._private[params.ableField]; + var changed = ele._private[params.field] != params.value; - ani_p.easingImpl = easings[name].apply(null, args); - } else { - // static impl by name - ani_p.easingImpl = easings[name]; - } - } - } + if (params.overrideAble) { + var overrideAble = params.overrideAble(ele); - var easing = ani_p.easingImpl; - var percent = void 0; + if (overrideAble !== undefined) { + able = overrideAble; - if (ani_p.duration === 0) { - percent = 1; - } else { - percent = (now - startTime) / ani_p.duration; - } + if (!overrideAble) { + return this; + } // to save cycles assume not able for all on override + } + } - if (ani_p.applying) { - percent = ani_p.progress; - } + if (able) { + ele._private[params.field] = params.value; - if (percent < 0) { - percent = 0; - } else if (percent > 1) { - percent = 1; - } + if (changed) { + changedEles.push(ele); + } + } + } - if (ani_p.delay == null) { - // then update + var changedColl = this.spawn(changedEles); + changedColl.updateStyle(); // change of state => possible change of style + changedColl.emit(params.event); + } - var startPos = ani_p.startPosition; - var endPos = ani_p.position; + return this; + }; +} - if (endPos && isEles && !self.locked()) { - var pos = self.position(); +function defineSwitchSet(params) { + elesfn[params.field] = function () { + var ele = this[0]; - if (valid(startPos.x, endPos.x)) { - pos.x = ease(startPos.x, endPos.x, percent, easing); - } + if (ele) { + if (params.overrideField) { + var val = params.overrideField(ele); - if (valid(startPos.y, endPos.y)) { - pos.y = ease(startPos.y, endPos.y, percent, easing); + if (val !== undefined) { + return val; + } } - self.emit('position'); + return ele._private[params.field]; } + }; - var startPan = ani_p.startPan; - var endPan = ani_p.pan; - var pan = _p.pan; - var animatingPan = endPan != null && isCore; - if (animatingPan) { - if (valid(startPan.x, endPan.x)) { - pan.x = ease(startPan.x, endPan.x, percent, easing); - } - - if (valid(startPan.y, endPan.y)) { - pan.y = ease(startPan.y, endPan.y, percent, easing); - } + elesfn[params.on] = defineSwitchFunction({ + event: params.on, + field: params.field, + ableField: params.ableField, + overrideAble: params.overrideAble, + value: true + }); - self.emit('pan'); - } + elesfn[params.off] = defineSwitchFunction({ + event: params.off, + field: params.field, + ableField: params.ableField, + overrideAble: params.overrideAble, + value: false + }); +} - var startZoom = ani_p.startZoom; - var endZoom = ani_p.zoom; - var animatingZoom = endZoom != null && isCore; - if (animatingZoom) { - if (valid(startZoom, endZoom)) { - _p.zoom = ease(startZoom, endZoom, percent, easing); - } +defineSwitchSet({ + field: 'locked', + overrideField: function overrideField(ele) { + return ele.cy().autolock() ? true : undefined; + }, + on: 'lock', + off: 'unlock' +}); - self.emit('zoom'); - } +defineSwitchSet({ + field: 'grabbable', + overrideField: function overrideField(ele) { + return ele.cy().autoungrabify() ? false : undefined; + }, + on: 'grabify', + off: 'ungrabify' +}); - if (animatingPan || animatingZoom) { - self.emit('viewport'); - } +defineSwitchSet({ + field: 'selected', + ableField: 'selectable', + overrideAble: function overrideAble(ele) { + return ele.cy().autounselectify() ? false : undefined; + }, + on: 'select', + off: 'unselect' +}); - var props = ani_p.style; - if (props && props.length > 0 && isEles) { - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - var _name = prop.name; - var end = prop; - var start = ani_p.startStyle[_name]; - var propSpec = style.properties[start.name]; - var easedVal = ease(start, end, percent, easing, propSpec); +defineSwitchSet({ + field: 'selectable', + overrideField: function overrideField(ele) { + return ele.cy().autounselectify() ? false : undefined; + }, + on: 'selectify', + off: 'unselectify' +}); - style.overrideBypass(self, _name, easedVal); - } // for props +elesfn.deselect = elesfn.unselect; - self.emit('style'); - } // if +elesfn.grabbed = function () { + var ele = this[0]; + if (ele) { + return ele._private.grabbed; } +}; - ani_p.progress = percent; - - return percent; -} - -function valid(start, end) { - if (start == null || end == null) { - return false; - } +defineSwitchSet({ + field: 'active', + on: 'activate', + off: 'unactivate' +}); - if (is.number(start) && is.number(end)) { - return true; - } else if (start && end) { - return true; +elesfn.inactive = function () { + var ele = this[0]; + if (ele) { + return !ele._private.active; } +}; - return false; -} - -module.exports = step; +module.exports = elesfn; /***/ }), -/* 64 */ +/* 68 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Emitter = __webpack_require__(10); -var define = __webpack_require__(3); -var is = __webpack_require__(0); var util = __webpack_require__(1); -var Selector = __webpack_require__(6); +var is = __webpack_require__(0); -var emitterOptions = { - qualifierCompare: function qualifierCompare(selector1, selector2) { - if (selector1 == null || selector2 == null) { - return selector1 == null && selector2 == null; - } else { - return selector1.sameText(selector2); - } - }, - eventMatches: function eventMatches(cy, listener, eventObj) { - var selector = listener.qualifier; +var elesfn = {}; - if (selector != null) { - return cy !== eventObj.target && is.element(eventObj.target) && selector.matches(eventObj.target); +var cache = function cache(fn, name) { + return function traversalCache(arg1, arg2, arg3, arg4) { + var selectorOrEles = arg1; + var eles = this; + var key = void 0; + + if (selectorOrEles == null) { + key = 'null'; + } else if (is.elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) { + key = '#' + selectorOrEles.id(); } - return true; - }, - eventFields: function eventFields(cy) { - return { - cy: cy, - target: cy - }; - }, - callbackContext: function callbackContext(cy, listener, eventObj) { - return listener.qualifier != null ? eventObj.target : cy; - } -}; + if (eles.length === 1 && key) { + var _p = eles[0]._private; + var tch = _p.traversalCache = _p.traversalCache || {}; + var ch = tch[name] = tch[name] || {}; + var cacheHit = ch[key]; -var argSelector = function argSelector(arg) { - if (is.string(arg)) { - return new Selector(arg); - } else { - return arg; - } + if (cacheHit) { + return cacheHit; + } else { + return ch[key] = fn.call(eles, arg1, arg2, arg3, arg4); + } + } else { + return fn.call(eles, arg1, arg2, arg3, arg4); + } + }; }; -var elesfn = { - createEmitter: function createEmitter() { - var _p = this._private; +// DAG functions +//////////////// - if (!_p.emitter) { - _p.emitter = new Emitter(util.assign({ - context: this - }, emitterOptions)); - } +var defineDagExtremity = function defineDagExtremity(params) { + return function dagExtremityImpl(selector) { + var eles = this; + var ret = []; - return this; - }, + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + if (!ele.isNode()) { + continue; + } - emitter: function emitter() { - return this._private.emitter; - }, + var disqualified = false; + var edges = ele.connectedEdges(); - on: function on(events, selector, callback) { - this.emitter().on(events, argSelector(selector), callback); + for (var j = 0; j < edges.length; j++) { + var edge = edges[j]; + var src = edge.source(); + var tgt = edge.target(); - return this; - }, + if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) { + disqualified = true; + break; + } + } - removeListener: function removeListener(events, selector, callback) { - this.emitter().removeListener(events, argSelector(selector), callback); + if (!disqualified) { + ret.push(ele); + } + } - return this; - }, + return this.spawn(ret, { unique: true }).filter(selector); + }; +}; - one: function one(events, selector, callback) { - this.emitter().one(events, argSelector(selector), callback); +var defineDagOneHop = function defineDagOneHop(params) { + return function (selector) { + var eles = this; + var oEles = []; - return this; - }, + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - once: function once(events, selector, callback) { - this.emitter().one(events, argSelector(selector), callback); + if (!ele.isNode()) { + continue; + } - return this; - }, + var edges = ele.connectedEdges(); + for (var j = 0; j < edges.length; j++) { + var edge = edges[j]; + var src = edge.source(); + var tgt = edge.target(); - emit: function emit(events, extraParams) { - this.emitter().emit(events, extraParams); + if (params.outgoing && src === ele) { + oEles.push(edge); + oEles.push(tgt); + } else if (params.incoming && tgt === ele) { + oEles.push(edge); + oEles.push(src); + } + } + } - return this; - } + return this.spawn(oEles, { unique: true }).filter(selector); + }; }; -define.eventAliasesOn(elesfn); - -module.exports = elesfn; - -/***/ }), -/* 65 */ -/***/ (function(module, exports, __webpack_require__) { +var defineDagAllHops = function defineDagAllHops(params) { + return function (selector) { + var eles = this; + var sEles = []; + var sElesIds = {}; -"use strict"; + for (;;) { + var next = params.outgoing ? eles.outgoers() : eles.incomers(); + if (next.length === 0) { + break; + } // done if none left -var corefn = { + var newNext = false; + for (var i = 0; i < next.length; i++) { + var n = next[i]; + var nid = n.id(); - png: function png(options) { - var renderer = this._private.renderer; - options = options || {}; + if (!sElesIds[nid]) { + sElesIds[nid] = true; + sEles.push(n); + newNext = true; + } + } - return renderer.png(options); - }, + if (!newNext) { + break; + } // done if touched all outgoers already - jpg: function jpg(options) { - var renderer = this._private.renderer; - options = options || {}; + eles = next; + } - options.bg = options.bg || '#fff'; + return this.spawn(sEles, { unique: true }).filter(selector); + }; +}; - return renderer.jpg(options); +elesfn.clearTraversalCache = function () { + for (var i = 0; i < this.length; i++) { + this[i]._private.traversalCache = null; } - }; -corefn.jpeg = corefn.jpg; +util.extend(elesfn, { + // get the root nodes in the DAG + roots: defineDagExtremity({ noIncomingEdges: true }), -module.exports = corefn; + // get the leaf nodes in the DAG + leaves: defineDagExtremity({ noOutgoingEdges: true }), -/***/ }), -/* 66 */ -/***/ (function(module, exports, __webpack_require__) { + // normally called children in graph theory + // these nodes =edges=> outgoing nodes + outgoers: cache(defineDagOneHop({ outgoing: true }), 'outgoers'), -"use strict"; + // aka DAG descendants + successors: defineDagAllHops({ outgoing: true }), + // normally called parents in graph theory + // these nodes <=edges= incoming nodes + incomers: cache(defineDagOneHop({ incoming: true }), 'incomers'), -var util = __webpack_require__(1); -var is = __webpack_require__(0); + // aka DAG ancestors + predecessors: defineDagAllHops({ incoming: true }) +}); -var corefn = { +// Neighbourhood functions +////////////////////////// - layout: function layout(options) { - var cy = this; +util.extend(elesfn, { + neighborhood: cache(function (selector) { + var elements = []; + var nodes = this.nodes(); - if (options == null) { - util.error('Layout options must be specified to make a layout'); - return; - } + for (var i = 0; i < nodes.length; i++) { + // for all nodes + var node = nodes[i]; + var connectedEdges = node.connectedEdges(); - if (options.name == null) { - util.error('A `name` must be specified to make a layout'); - return; - } + // for each connected edge, add the edge and the other node + for (var j = 0; j < connectedEdges.length; j++) { + var edge = connectedEdges[j]; + var src = edge.source(); + var tgt = edge.target(); + var otherNode = node === src ? tgt : src; - var name = options.name; - var Layout = cy.extension('layout', name); + // need check in case of loop + if (otherNode.length > 0) { + elements.push(otherNode[0]); // add node 1 hop away + } - if (Layout == null) { - util.error('Can not apply layout: No such layout `' + name + '` found; did you include its JS file?'); - return; + // add connected edge + elements.push(edge[0]); + } } - var eles = void 0; - if (is.string(options.eles)) { - eles = cy.$(options.eles); - } else { - eles = options.eles != null ? options.eles : cy.$(); - } + return this.spawn(elements, { unique: true }).filter(selector); + }, 'neighborhood'), - var layout = new Layout(util.extend({}, options, { - cy: cy, - eles: eles - })); + closedNeighborhood: function closedNeighborhood(selector) { + return this.neighborhood().add(this).filter(selector); + }, - return layout; + openNeighborhood: function openNeighborhood(selector) { + return this.neighborhood(selector); } +}); -}; - -corefn.createLayout = corefn.makeLayout = corefn.layout; +// aliases +elesfn.neighbourhood = elesfn.neighborhood; +elesfn.closedNeighbourhood = elesfn.closedNeighborhood; +elesfn.openNeighbourhood = elesfn.openNeighborhood; -module.exports = corefn; +// Edge functions +///////////////// -/***/ }), -/* 67 */ -/***/ (function(module, exports, __webpack_require__) { +util.extend(elesfn, { + source: cache(function sourceImpl(selector) { + var ele = this[0]; + var src = void 0; -"use strict"; + if (ele) { + src = ele._private.source || ele.cy().collection(); + } + return src && selector ? src.filter(selector) : src; + }, 'source'), -var corefn = { - notify: function notify(params) { - var _p = this._private; + target: cache(function targetImpl(selector) { + var ele = this[0]; + var tgt = void 0; - if (_p.batchingNotify) { - var bEles = _p.batchNotifyEles; - var bTypes = _p.batchNotifyTypes; + if (ele) { + tgt = ele._private.target || ele.cy().collection(); + } - if (params.eles) { - bEles.merge(params.eles); - } + return tgt && selector ? tgt.filter(selector) : tgt; + }, 'target'), - if (!bTypes.ids[params.type]) { - bTypes.push(params.type); - bTypes.ids[params.type] = true; - } + sources: defineSourceFunction({ + attr: 'source' + }), - return; // notifications are disabled during batching - } + targets: defineSourceFunction({ + attr: 'target' + }) +}); - if (!_p.notificationsEnabled) { - return; - } // exit on disabled +function defineSourceFunction(params) { + return function sourceImpl(selector) { + var sources = []; - var renderer = this.renderer(); + for (var i = 0; i < this.length; i++) { + var ele = this[i]; + var src = ele._private[params.attr]; - // exit if destroy() called on core or renderer in between frames #1499 #1528 - if (this.isDestroyed() || !renderer) { - return; + if (src) { + sources.push(src); + } } - renderer.notify(params); - }, - - notifications: function notifications(bool) { - var p = this._private; - - if (bool === undefined) { - return p.notificationsEnabled; - } else { - p.notificationsEnabled = bool ? true : false; - } - }, + return this.spawn(sources, { unique: true }).filter(selector); + }; +} - noNotifications: function noNotifications(callback) { - this.notifications(false); - callback(); - this.notifications(true); - }, +util.extend(elesfn, { + edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'), - batching: function batching() { - return this._private.batchCount > 0; - }, + edgesTo: cache(defineEdgesWithFunction({ + thisIsSrc: true + }), 'edgesTo') +}); - startBatch: function startBatch() { - var _p = this._private; +function defineEdgesWithFunction(params) { - if (_p.batchCount == null) { - _p.batchCount = 0; - } + return function edgesWithImpl(otherNodes) { + var elements = []; + var cy = this._private.cy; + var p = params || {}; - if (_p.batchCount === 0) { - _p.batchingStyle = _p.batchingNotify = true; - _p.batchStyleEles = this.collection(); - _p.batchNotifyEles = this.collection(); - _p.batchNotifyTypes = []; - _p.batchNotifyTypes.ids = {}; + // get elements if a selector is specified + if (is.string(otherNodes)) { + otherNodes = cy.$(otherNodes); } - _p.batchCount++; + for (var h = 0; h < otherNodes.length; h++) { + var edges = otherNodes[h]._private.edges; - return this; - }, + for (var i = 0; i < edges.length; i++) { + var edge = edges[i]; + var edgeData = edge._private.data; + var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target); + var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target); + var edgeConnectsThisAndOther = thisToOther || otherToThis; - endBatch: function endBatch() { - var _p = this._private; + if (!edgeConnectsThisAndOther) { + continue; + } - _p.batchCount--; + if (p.thisIsSrc || p.thisIsTgt) { + if (p.thisIsSrc && !thisToOther) { + continue; + } - if (_p.batchCount === 0) { - // update style for dirty eles - _p.batchingStyle = false; - _p.batchStyleEles.updateStyle(); + if (p.thisIsTgt && !otherToThis) { + continue; + } + } - // notify the renderer of queued eles and event types - _p.batchingNotify = false; - this.notify({ - type: _p.batchNotifyTypes, - eles: _p.batchNotifyEles - }); + elements.push(edge); + } } - return this; - }, - - batch: function batch(callback) { - this.startBatch(); - callback(); - this.endBatch(); - - return this; - }, - - // for backwards compatibility - batchData: function batchData(map) { - var cy = this; - - return this.batch(function () { - var ids = Object.keys(map); + return this.spawn(elements, { unique: true }); + }; +} - for (var i = 0; i < ids.length; i++) { - var id = ids[i]; - var data = map[id]; - var ele = cy.getElementById(id); +util.extend(elesfn, { + connectedEdges: cache(function (selector) { + var retEles = []; - ele.data(data); + var eles = this; + for (var i = 0; i < eles.length; i++) { + var node = eles[i]; + if (!node.isNode()) { + continue; } - }); - } -}; - -module.exports = corefn; - -/***/ }), -/* 68 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; + var edges = node._private.edges; + for (var j = 0; j < edges.length; j++) { + var edge = edges[j]; + retEles.push(edge); + } + } -var util = __webpack_require__(1); + return this.spawn(retEles, { unique: true }).filter(selector); + }, 'connectedEdges'), -var corefn = { + connectedNodes: cache(function (selector) { + var retEles = []; - renderTo: function renderTo(context, zoom, pan, pxRatio) { - var r = this._private.renderer; + var eles = this; + for (var i = 0; i < eles.length; i++) { + var edge = eles[i]; + if (!edge.isEdge()) { + continue; + } - r.renderTo(context, zoom, pan, pxRatio); - return this; - }, + retEles.push(edge.source()[0]); + retEles.push(edge.target()[0]); + } - renderer: function renderer() { - return this._private.renderer; - }, + return this.spawn(retEles, { unique: true }).filter(selector); + }, 'connectedNodes'), - forceRender: function forceRender() { - this.notify({ - type: 'draw' - }); + parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'), - return this; - }, + codirectedEdges: cache(defineParallelEdgesFunction({ + codirected: true + }), 'codirectedEdges') +}); - resize: function resize() { - this.invalidateSize(); +function defineParallelEdgesFunction(params) { + var defaults = { + codirected: false + }; + params = util.extend({}, defaults, params); - this.notify({ - type: 'resize' - }); + return function parallelEdgesImpl(selector) { + // micro-optimised for renderer + var elements = []; + var edges = this.edges(); + var p = params; - this.emit('resize'); + // look at all the edges in the collection + for (var i = 0; i < edges.length; i++) { + var edge1 = edges[i]; + var edge1_p = edge1._private; + var src1 = edge1_p.source; + var srcid1 = src1._private.data.id; + var tgtid1 = edge1_p.data.target; + var srcEdges1 = src1._private.edges; - return this; - }, + // look at edges connected to the src node of this edge + for (var j = 0; j < srcEdges1.length; j++) { + var edge2 = srcEdges1[j]; + var edge2data = edge2._private.data; + var tgtid2 = edge2data.target; + var srcid2 = edge2data.source; - initRenderer: function initRenderer(options) { - var cy = this; + var codirected = tgtid2 === tgtid1 && srcid2 === srcid1; + var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2; - var RendererProto = cy.extension('renderer', options.name); - if (RendererProto == null) { - util.error('Can not initialise: No such renderer `%s` found; did you include its JS file?', options.name); - return; + if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) { + elements.push(edge2); + } + } } - cy._private.renderer = new RendererProto(util.extend({}, options, { cy: cy })); - - this.notify({ type: 'init' }); - }, + return this.spawn(elements, { unique: true }).filter(selector); + }; +} - destroyRenderer: function destroyRenderer() { - var cy = this; +// Misc functions +///////////////// - cy.notify({ type: 'destroy' }); // destroy the renderer +util.extend(elesfn, { + components: function components() { + var self = this; + var cy = self.cy(); + var visited = self.spawn(); + var unvisited = self.nodes().spawnSelf(); + var components = []; - var domEle = cy.container(); - if (domEle) { - domEle._cyreg = null; + var visitInComponent = function visitInComponent(node, component) { + visited.merge(node); + unvisited.unmerge(node); + component.merge(node); + }; - while (domEle.childNodes.length > 0) { - domEle.removeChild(domEle.childNodes[0]); - } + if (unvisited.empty()) { + return self.spawn(); } - cy._private.renderer = null; // to be extra safe, remove the ref - }, + var _loop = function _loop() { + var component = cy.collection(); + components.push(component); - onRender: function onRender(fn) { - return this.on('render', fn); - }, + var root = unvisited[0]; + visitInComponent(root, component); - offRender: function offRender(fn) { - return this.off('render', fn); - } + self.bfs({ + directed: false, + roots: root, + visit: function visit(v, e, u, i, depth) { + visitInComponent(v, component); + } + }); + }; -}; + do { + _loop(); + } while (unvisited.length > 0); -corefn.invalidateDimensions = corefn.resize; + return components.map(function (component) { + var connectedEdges = component.connectedEdges().stdFilter(function (edge) { + return component.anySame(edge.source()) && component.anySame(edge.target()); + }); -module.exports = corefn; + return component.union(connectedEdges); + }); + } +}); + +module.exports = elesfn; /***/ }), /* 69 */ @@ -12167,71 +12427,87 @@ module.exports = corefn; var is = __webpack_require__(0); +var util = __webpack_require__(1); var Collection = __webpack_require__(7); +var Element = __webpack_require__(14); var corefn = { + add: function add(opts) { - // get a collection - // - empty collection on no args - // - collection of elements in the graph on selector arg - // - guarantee a returned collection when elements or collection specified - collection: function collection(eles, opts) { + var elements = void 0; + var cy = this; - if (is.string(eles)) { - return this.$(eles); - } else if (is.elementOrCollection(eles)) { - return eles.collection(); - } else if (is.array(eles)) { - return new Collection(this, eles, opts); - } + // add the elements + if (is.elementOrCollection(opts)) { + var eles = opts; - return new Collection(this); - }, + if (eles._private.cy === cy) { + // same instance => just restore + elements = eles.restore(); + } else { + // otherwise, copy from json + var jsons = []; - nodes: function nodes(selector) { - var nodes = this.$(function (ele) { - return ele.isNode(); - }); + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + jsons.push(ele.json()); + } - if (selector) { - return nodes.filter(selector); + elements = new Collection(cy, jsons); + } } - return nodes; - }, + // specify an array of options + else if (is.array(opts)) { + var _jsons = opts; - edges: function edges(selector) { - var edges = this.$(function (ele) { - return ele.isEdge(); - }); + elements = new Collection(cy, _jsons); + } - if (selector) { - return edges.filter(selector); - } + // specify via opts.nodes and opts.edges + else if (is.plainObject(opts) && (is.array(opts.nodes) || is.array(opts.edges))) { + var elesByGroup = opts; + var _jsons2 = []; - return edges; - }, + var grs = ['nodes', 'edges']; + for (var _i = 0, il = grs.length; _i < il; _i++) { + var group = grs[_i]; + var elesArray = elesByGroup[group]; - // search the graph like jQuery - $: function $(selector) { - var eles = this._private.elements; + if (is.array(elesArray)) { - if (selector) { - return eles.filter(selector); - } else { - return eles.spawnSelf(); - } + for (var j = 0, jl = elesArray.length; j < jl; j++) { + var json = util.extend({ group: group }, elesArray[j]); + + _jsons2.push(json); + } + } + } + + elements = new Collection(cy, _jsons2); + } + + // specify options for one element + else { + var _json = opts; + elements = new Element(cy, _json).collection(); + } + + return elements; }, - mutableElements: function mutableElements() { - return this._private.elements; - } + remove: function remove(collection) { + if (is.elementOrCollection(collection)) { + // already have right ref + } else if (is.string(collection)) { + var selector = collection; + collection = this.$(selector); + } + return collection.remove(); + } }; -// aliases -corefn.elements = corefn.filter = corefn.$; - module.exports = corefn; /***/ }), @@ -12241,36 +12517,71 @@ module.exports = corefn; "use strict"; -var is = __webpack_require__(0); -var Style = __webpack_require__(18); +var define = __webpack_require__(4); +var util = __webpack_require__(1); +var stepAll = __webpack_require__(71); var corefn = { - style: function style(newStyle) { - if (newStyle) { - var s = this.setStyle(newStyle); + // pull in animation functions + animate: define.animate(), + animation: define.animation(), + animated: define.animated(), + clearQueue: define.clearQueue(), + delay: define.delay(), + delayAnimation: define.delayAnimation(), + stop: define.stop(), - s.update(); - } + addToAnimationPool: function addToAnimationPool(eles) { + var cy = this; - return this._private.style; + if (!cy.styleEnabled()) { + return; + } // save cycles when no style used + + cy._private.aniEles.merge(eles); }, - setStyle: function setStyle(style) { - var _p = this._private; + stopAnimationLoop: function stopAnimationLoop() { + this._private.animationsRunning = false; + }, - if (is.stylesheet(style)) { - _p.style = style.generateStyle(this); - } else if (is.array(style)) { - _p.style = Style.fromJson(this, style); - } else if (is.string(style)) { - _p.style = Style.fromString(this, style); - } else { - _p.style = Style(this); + startAnimationLoop: function startAnimationLoop() { + var cy = this; + + cy._private.animationsRunning = true; + + if (!cy.styleEnabled()) { + return; + } // save cycles when no style used + + // NB the animation loop will exec in headless environments if style enabled + // and explicit cy.destroy() is necessary to stop the loop + + function headlessStep() { + if (!cy._private.animationsRunning) { + return; + } + + util.requestAnimationFrame(function animationStep(now) { + stepAll(now, cy); + headlessStep(); + }); } - return _p.style; + var renderer = cy.renderer(); + + if (renderer && renderer.beforeRender) { + // let the renderer schedule animations + renderer.beforeRender(function rendererAnimationStep(willDraw, now) { + stepAll(now, cy); + }, renderer.beforeRenderPriorities.animations); + } else { + // manage the animation loop ourselves + headlessStep(); // first call + } } + }; module.exports = corefn; @@ -12282,16667 +12593,16160 @@ module.exports = corefn; "use strict"; -var is = __webpack_require__(0); -var window = __webpack_require__(4); -var math = __webpack_require__(2); +var step = __webpack_require__(72); +var startAnimation = __webpack_require__(77); -var corefn = { +function stepAll(now, cy) { + var eles = cy._private.aniEles; + var doneEles = []; - autolock: function autolock(bool) { - if (bool !== undefined) { - this._private.autolock = bool ? true : false; - } else { - return this._private.autolock; - } + function stepOne(ele, isCore) { + var _p = ele._private; + var current = _p.animation.current; + var queue = _p.animation.queue; + var ranAnis = false; - return this; // chaining - }, + // cancel all animations on display:none ele + if (!isCore && ele.pstyle('display').value === 'none') { + // put all current and queue animations in this tick's current list + // and empty the lists for the element + current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); - autoungrabify: function autoungrabify(bool) { - if (bool !== undefined) { - this._private.autoungrabify = bool ? true : false; - } else { - return this._private.autoungrabify; + // stop all animations + for (var i = 0; i < current.length; i++) { + current[i].stop(); + } } - return this; // chaining - }, + // if nothing currently animating, get something from the queue + if (current.length === 0) { + var next = queue.shift(); - autounselectify: function autounselectify(bool) { - if (bool !== undefined) { - this._private.autounselectify = bool ? true : false; - } else { - return this._private.autounselectify; - } - - return this; // chaining - }, - - panningEnabled: function panningEnabled(bool) { - if (bool !== undefined) { - this._private.panningEnabled = bool ? true : false; - } else { - return this._private.panningEnabled; + if (next) { + current.push(next); + } } - return this; // chaining - }, - - userPanningEnabled: function userPanningEnabled(bool) { - if (bool !== undefined) { - this._private.userPanningEnabled = bool ? true : false; - } else { - return this._private.userPanningEnabled; - } + var callbacks = function callbacks(_callbacks) { + for (var j = _callbacks.length - 1; j >= 0; j--) { + var cb = _callbacks[j]; - return this; // chaining - }, + cb(); + } - zoomingEnabled: function zoomingEnabled(bool) { - if (bool !== undefined) { - this._private.zoomingEnabled = bool ? true : false; - } else { - return this._private.zoomingEnabled; - } + _callbacks.splice(0, _callbacks.length); + }; - return this; // chaining - }, + // step and remove if done + for (var _i = current.length - 1; _i >= 0; _i--) { + var ani = current[_i]; + var ani_p = ani._private; - userZoomingEnabled: function userZoomingEnabled(bool) { - if (bool !== undefined) { - this._private.userZoomingEnabled = bool ? true : false; - } else { - return this._private.userZoomingEnabled; - } + if (ani_p.stopped) { + current.splice(_i, 1); - return this; // chaining - }, + ani_p.hooked = false; + ani_p.playing = false; + ani_p.started = false; - boxSelectionEnabled: function boxSelectionEnabled(bool) { - if (bool !== undefined) { - this._private.boxSelectionEnabled = bool ? true : false; - } else { - return this._private.boxSelectionEnabled; - } + callbacks(ani_p.frames); - return this; // chaining - }, + continue; + } - pan: function pan() { - var args = arguments; - var pan = this._private.pan; - var dim = void 0, - val = void 0, - dims = void 0, - x = void 0, - y = void 0; + if (!ani_p.playing && !ani_p.applying) { + continue; + } - switch (args.length) { - case 0: - // .pan() - return pan; + // an apply() while playing shouldn't do anything + if (ani_p.playing && ani_p.applying) { + ani_p.applying = false; + } - case 1: + if (!ani_p.started) { + startAnimation(ele, ani, now, isCore); + } - if (is.string(args[0])) { - // .pan('x') - dim = args[0]; - return pan[dim]; - } else if (is.plainObject(args[0])) { - // .pan({ x: 0, y: 100 }) - if (!this._private.panningEnabled) { - return this; - } + step(ele, ani, now, isCore); - dims = args[0]; - x = dims.x; - y = dims.y; + if (ani_p.applying) { + ani_p.applying = false; + } - if (is.number(x)) { - pan.x = x; - } + callbacks(ani_p.frames); - if (is.number(y)) { - pan.y = y; - } + if (ani.completed()) { + current.splice(_i, 1); - this.emit('pan viewport'); - } - break; + ani_p.hooked = false; + ani_p.playing = false; + ani_p.started = false; - case 2: - // .pan('x', 100) - if (!this._private.panningEnabled) { - return this; - } + callbacks(ani_p.completes); + } - dim = args[0]; - val = args[1]; + ranAnis = true; + } - if ((dim === 'x' || dim === 'y') && is.number(val)) { - pan[dim] = val; - } + if (!isCore && current.length === 0 && queue.length === 0) { + doneEles.push(ele); + } - this.emit('pan viewport'); - break; + return ranAnis; + } // stepElement - default: - break; // invalid - } + // handle all eles + var ranEleAni = false; + for (var e = 0; e < eles.length; e++) { + var ele = eles[e]; + var handledThisEle = stepOne(ele); - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); + ranEleAni = ranEleAni || handledThisEle; + } // each element - return this; // chaining - }, + var ranCoreAni = stepOne(cy, true); - panBy: function panBy(arg0, arg1) { - var args = arguments; - var pan = this._private.pan; - var dim = void 0, - val = void 0, - dims = void 0, - x = void 0, - y = void 0; + // notify renderer + if (ranEleAni || ranCoreAni) { + if (eles.length > 0) { + eles.dirtyCompoundBoundsCache(); - if (!this._private.panningEnabled) { - return this; + cy.notify({ + type: 'draw', + eles: eles + }); + } else { + cy.notify({ + type: 'draw' + }); } + } - switch (args.length) { - case 1: - - if (is.plainObject(arg0)) { - // .panBy({ x: 0, y: 100 }) - dims = args[0]; - x = dims.x; - y = dims.y; + // remove elements from list of currently animating if its queues are empty + eles.unmerge(doneEles); - if (is.number(x)) { - pan.x += x; - } + cy.emit('step'); +} // stepAll - if (is.number(y)) { - pan.y += y; - } +module.exports = stepAll; - this.emit('pan viewport'); - } - break; +/***/ }), +/* 72 */ +/***/ (function(module, exports, __webpack_require__) { - case 2: - // .panBy('x', 100) - dim = arg0; - val = arg1; +"use strict"; - if ((dim === 'x' || dim === 'y') && is.number(val)) { - pan[dim] += val; - } - this.emit('pan viewport'); - break; +var easings = __webpack_require__(73); +var ease = __webpack_require__(76); +var is = __webpack_require__(0); - default: - break; // invalid - } +function step(self, ani, now, isCore) { + var isEles = !isCore; + var _p = self._private; + var ani_p = ani._private; + var pEasing = ani_p.easing; + var startTime = ani_p.startTime; + var cy = isCore ? self : self.cy(); + var style = cy.style(); - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); + if (!ani_p.easingImpl) { - return this; // chaining - }, + if (pEasing == null) { + // use default + ani_p.easingImpl = easings['linear']; + } else { + // then define w/ name + var easingVals = void 0; - fit: function fit(elements, padding) { - var viewportState = this.getFitViewport(elements, padding); + if (is.string(pEasing)) { + var easingProp = style.parse('transition-timing-function', pEasing); - if (viewportState) { - var _p = this._private; - _p.zoom = viewportState.zoom; - _p.pan = viewportState.pan; + easingVals = easingProp.value; + } else { + // then assume preparsed array + easingVals = pEasing; + } - this.emit('pan zoom viewport'); + var name = void 0, + args = void 0; - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); - } - - return this; // chaining - }, + if (is.string(easingVals)) { + name = easingVals; + args = []; + } else { + name = easingVals[1]; + args = easingVals.slice(2).map(function (n) { + return +n; + }); + } - getFitViewport: function getFitViewport(elements, padding) { - if (is.number(elements) && padding === undefined) { - // elements is optional - padding = elements; - elements = undefined; - } + if (args.length > 0) { + // create with args + if (name === 'spring') { + args.push(ani_p.duration); // need duration to generate spring + } - if (!this._private.panningEnabled || !this._private.zoomingEnabled) { - return; + ani_p.easingImpl = easings[name].apply(null, args); + } else { + // static impl by name + ani_p.easingImpl = easings[name]; + } } + } - var bb = void 0; + var easing = ani_p.easingImpl; + var percent = void 0; - if (is.string(elements)) { - var sel = elements; - elements = this.$(sel); - } else if (is.boundingBox(elements)) { - // assume bb - var bbe = elements; - bb = { - x1: bbe.x1, - y1: bbe.y1, - x2: bbe.x2, - y2: bbe.y2 - }; + if (ani_p.duration === 0) { + percent = 1; + } else { + percent = (now - startTime) / ani_p.duration; + } - bb.w = bb.x2 - bb.x1; - bb.h = bb.y2 - bb.y1; - } else if (!is.elementOrCollection(elements)) { - elements = this.mutableElements(); - } + if (ani_p.applying) { + percent = ani_p.progress; + } - if (is.elementOrCollection(elements) && elements.empty()) { - return; - } // can't fit to nothing + if (percent < 0) { + percent = 0; + } else if (percent > 1) { + percent = 1; + } - bb = bb || elements.boundingBox(); + if (ani_p.delay == null) { + // then update - var w = this.width(); - var h = this.height(); - var zoom = void 0; - padding = is.number(padding) ? padding : 0; + var startPos = ani_p.startPosition; + var endPos = ani_p.position; - if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) { - zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); + if (endPos && isEles && !self.locked()) { + var pos = self.position(); - // crop zoom - zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom; - zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom; + if (valid(startPos.x, endPos.x)) { + pos.x = ease(startPos.x, endPos.x, percent, easing); + } - var pan = { // now pan to middle - x: (w - zoom * (bb.x1 + bb.x2)) / 2, - y: (h - zoom * (bb.y1 + bb.y2)) / 2 - }; + if (valid(startPos.y, endPos.y)) { + pos.y = ease(startPos.y, endPos.y, percent, easing); + } - return { - zoom: zoom, - pan: pan - }; + self.emit('position'); } - return; - }, - - minZoom: function minZoom(zoom) { - if (zoom === undefined) { - return this._private.minZoom; - } else if (is.number(zoom)) { - this._private.minZoom = zoom; - } + var startPan = ani_p.startPan; + var endPan = ani_p.pan; + var pan = _p.pan; + var animatingPan = endPan != null && isCore; + if (animatingPan) { + if (valid(startPan.x, endPan.x)) { + pan.x = ease(startPan.x, endPan.x, percent, easing); + } - return this; - }, + if (valid(startPan.y, endPan.y)) { + pan.y = ease(startPan.y, endPan.y, percent, easing); + } - maxZoom: function maxZoom(zoom) { - if (zoom === undefined) { - return this._private.maxZoom; - } else if (is.number(zoom)) { - this._private.maxZoom = zoom; + self.emit('pan'); } - return this; - }, + var startZoom = ani_p.startZoom; + var endZoom = ani_p.zoom; + var animatingZoom = endZoom != null && isCore; + if (animatingZoom) { + if (valid(startZoom, endZoom)) { + _p.zoom = ease(startZoom, endZoom, percent, easing); + } - getZoomedViewport: function getZoomedViewport(params) { - var _p = this._private; - var currentPan = _p.pan; - var currentZoom = _p.zoom; - var pos = void 0; // in rendered px - var zoom = void 0; - var bail = false; + self.emit('zoom'); + } - if (!_p.zoomingEnabled) { - // zooming disabled - bail = true; + if (animatingPan || animatingZoom) { + self.emit('viewport'); } - if (is.number(params)) { - // then set the zoom - zoom = params; - } else if (is.plainObject(params)) { - // then zoom about a point - zoom = params.level; + var props = ani_p.style; + if (props && props.length > 0 && isEles) { + for (var i = 0; i < props.length; i++) { + var prop = props[i]; + var _name = prop.name; + var end = prop; + var start = ani_p.startStyle[_name]; + var propSpec = style.properties[start.name]; + var easedVal = ease(start, end, percent, easing, propSpec); - if (params.position != null) { - pos = math.modelToRenderedPosition(params.position, currentZoom, currentPan); - } else if (params.renderedPosition != null) { - pos = params.renderedPosition; - } + style.overrideBypass(self, _name, easedVal); + } // for props - if (pos != null && !_p.panningEnabled) { - // panning disabled - bail = true; - } - } + self.emit('style'); + } // if + } - // crop zoom - zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom; - zoom = zoom < _p.minZoom ? _p.minZoom : zoom; + ani_p.progress = percent; - // can't zoom with invalid params - if (bail || !is.number(zoom) || zoom === currentZoom || pos != null && (!is.number(pos.x) || !is.number(pos.y))) { - return null; - } + return percent; +} - if (pos != null) { - // set zoom about position - var pan1 = currentPan; - var zoom1 = currentZoom; - var zoom2 = zoom; +function valid(start, end) { + if (start == null || end == null) { + return false; + } - var pan2 = { - x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x, - y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y - }; + if (is.number(start) && is.number(end)) { + return true; + } else if (start && end) { + return true; + } - return { - zoomed: true, - panned: true, - zoom: zoom2, - pan: pan2 - }; - } else { - // just set the zoom - return { - zoomed: true, - panned: false, - zoom: zoom, - pan: currentPan - }; - } - }, + return false; +} - zoom: function zoom(params) { - if (params === undefined) { - // get - return this._private.zoom; - } else { - // set - var vp = this.getZoomedViewport(params); - var _p = this._private; +module.exports = step; - if (vp == null || !vp.zoomed) { - return this; - } +/***/ }), +/* 73 */ +/***/ (function(module, exports, __webpack_require__) { - _p.zoom = vp.zoom; +"use strict"; - if (vp.panned) { - _p.pan.x = vp.pan.x; - _p.pan.y = vp.pan.y; - } - this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport'); +var generateCubicBezier = __webpack_require__(74); +var generateSpringRK4 = __webpack_require__(75); - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); +var cubicBezier = function cubicBezier(t1, p1, t2, p2) { + var bezier = generateCubicBezier(t1, p1, t2, p2); - return this; // chaining - } - }, + return function (start, end, percent) { + return start + (end - start) * bezier(percent); + }; +}; - viewport: function viewport(opts) { - var _p = this._private; - var zoomDefd = true; - var panDefd = true; - var events = []; // to trigger - var zoomFailed = false; - var panFailed = false; +var easings = { + 'linear': function linear(start, end, percent) { + return start + (end - start) * percent; + }, - if (!opts) { - return this; - } - if (!is.number(opts.zoom)) { - zoomDefd = false; - } - if (!is.plainObject(opts.pan)) { - panDefd = false; - } - if (!zoomDefd && !panDefd) { - return this; - } + // default easings + 'ease': cubicBezier(0.25, 0.1, 0.25, 1), + 'ease-in': cubicBezier(0.42, 0, 1, 1), + 'ease-out': cubicBezier(0, 0, 0.58, 1), + 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1), - if (zoomDefd) { - var z = opts.zoom; + // sine + 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715), + 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1), + 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95), - if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) { - zoomFailed = true; - } else { - _p.zoom = z; + // quad + 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53), + 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94), + 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955), - events.push('zoom'); - } - } + // cubic + 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19), + 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1), + 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1), - if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) { - var p = opts.pan; + // quart + 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22), + 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1), + 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1), - if (is.number(p.x)) { - _p.pan.x = p.x; - panFailed = false; - } + // quint + 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06), + 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1), + 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1), - if (is.number(p.y)) { - _p.pan.y = p.y; - panFailed = false; - } + // expo + 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035), + 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1), + 'ease-in-out-expo': cubicBezier(1, 0, 0, 1), - if (!panFailed) { - events.push('pan'); - } - } + // circ + 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335), + 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1), + 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86), - if (events.length > 0) { - events.push('viewport'); - this.emit(events.join(' ')); + // user param easings... - this.notify({ - type: 'viewport' - }); + 'spring': function spring(tension, friction, duration) { + if (duration === 0) { + // can't get a spring w/ duration 0 + return easings.linear; // duration 0 => jump to end so impl doesn't matter } - return this; // chaining - }, - - center: function center(elements) { - var pan = this.getCenterPan(elements); - - if (pan) { - this._private.pan = pan; + var spring = generateSpringRK4(tension, friction, duration); - this.emit('pan viewport'); + return function (start, end, percent) { + return start + (end - start) * spring(percent); + }; + }, - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); - } + 'cubic-bezier': cubicBezier +}; - return this; // chaining - }, +module.exports = easings; - getCenterPan: function getCenterPan(elements, zoom) { - if (!this._private.panningEnabled) { - return; - } +/***/ }), +/* 74 */ +/***/ (function(module, exports, __webpack_require__) { - if (is.string(elements)) { - var selector = elements; - elements = this.mutableElements().filter(selector); - } else if (!is.elementOrCollection(elements)) { - elements = this.mutableElements(); - } +"use strict"; - if (elements.length === 0) { - return; - } // can't centre pan to nothing - var bb = elements.boundingBox(); - var w = this.width(); - var h = this.height(); - zoom = zoom === undefined ? this._private.zoom : zoom; +/* global Float32Array */ - var pan = { // middle - x: (w - zoom * (bb.x1 + bb.x2)) / 2, - y: (h - zoom * (bb.y1 + bb.y2)) / 2 - }; +/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */ +function generateCubicBezier(mX1, mY1, mX2, mY2) { + var NEWTON_ITERATIONS = 4, + NEWTON_MIN_SLOPE = 0.001, + SUBDIVISION_PRECISION = 0.0000001, + SUBDIVISION_MAX_ITERATIONS = 10, + kSplineTableSize = 11, + kSampleStepSize = 1.0 / (kSplineTableSize - 1.0), + float32ArraySupported = typeof Float32Array !== 'undefined'; - return pan; - }, + /* Must contain four arguments. */ + if (arguments.length !== 4) { + return false; + } - reset: function reset() { - if (!this._private.panningEnabled || !this._private.zoomingEnabled) { - return this; + /* Arguments must be numbers. */ + for (var i = 0; i < 4; ++i) { + if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) { + return false; } + } - this.viewport({ - pan: { x: 0, y: 0 }, - zoom: 1 - }); - - return this; // chaining - }, + /* X values must be in the [0, 1] range. */ + mX1 = Math.min(mX1, 1); + mX2 = Math.min(mX2, 1); + mX1 = Math.max(mX1, 0); + mX2 = Math.max(mX2, 0); - invalidateSize: function invalidateSize() { - this._private.sizeCache = null; - }, + var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); - size: function size() { - var _p = this._private; - var container = _p.container; + function A(aA1, aA2) { + return 1.0 - 3.0 * aA2 + 3.0 * aA1; + } - return _p.sizeCache = _p.sizeCache || (container ? function () { - var style = window.getComputedStyle(container); - var val = function val(name) { - return parseFloat(style.getPropertyValue(name)); - }; + function B(aA1, aA2) { + return 3.0 * aA2 - 6.0 * aA1; + } - return { - width: container.clientWidth - val('padding-left') - val('padding-right'), - height: container.clientHeight - val('padding-top') - val('padding-bottom') - }; - }() : { // fallback if no container (not 0 b/c can be used for dividing etc) - width: 1, - height: 1 - }); - }, + function C(aA1) { + return 3.0 * aA1; + } - width: function width() { - return this.size().width; - }, + function calcBezier(aT, aA1, aA2) { + return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; + } - height: function height() { - return this.size().height; - }, + function getSlope(aT, aA1, aA2) { + return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); + } - extent: function extent() { - var pan = this._private.pan; - var zoom = this._private.zoom; - var rb = this.renderedExtent(); + function newtonRaphsonIterate(aX, aGuessT) { + for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) { + var currentSlope = getSlope(aGuessT, mX1, mX2); - var b = { - x1: (rb.x1 - pan.x) / zoom, - x2: (rb.x2 - pan.x) / zoom, - y1: (rb.y1 - pan.y) / zoom, - y2: (rb.y2 - pan.y) / zoom - }; + if (currentSlope === 0.0) { + return aGuessT; + } - b.w = b.x2 - b.x1; - b.h = b.y2 - b.y1; + var currentX = calcBezier(aGuessT, mX1, mX2) - aX; + aGuessT -= currentX / currentSlope; + } - return b; - }, + return aGuessT; + } - renderedExtent: function renderedExtent() { - var width = this.width(); - var height = this.height(); + function calcSampleValues() { + for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) { + mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2); + } + } - return { - x1: 0, - y1: 0, - x2: width, - y2: height, - w: width, - h: height - }; - } -}; + function binarySubdivide(aX, aA, aB) { + var currentX = void 0, + currentT = void 0, + i = 0; -// aliases -corefn.centre = corefn.center; + do { + currentT = aA + (aB - aA) / 2.0; + currentX = calcBezier(currentT, mX1, mX2) - aX; + if (currentX > 0.0) { + aB = currentT; + } else { + aA = currentT; + } + } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); -// backwards compatibility -corefn.autolockNodes = corefn.autolock; -corefn.autoungrabifyNodes = corefn.autoungrabify; + return currentT; + } -module.exports = corefn; + function getTForX(aX) { + var intervalStart = 0.0, + currentSample = 1, + lastSample = kSplineTableSize - 1; -/***/ }), -/* 72 */ -/***/ (function(module, exports, __webpack_require__) { + for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) { + intervalStart += kSampleStepSize; + } -"use strict"; + --currentSample; + var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]), + guessForT = intervalStart + dist * kSampleStepSize, + initialSlope = getSlope(guessForT, mX1, mX2); -var util = __webpack_require__(1); -var Animation = __webpack_require__(23); -var math = __webpack_require__(2); -var is = __webpack_require__(0); + if (initialSlope >= NEWTON_MIN_SLOPE) { + return newtonRaphsonIterate(aX, guessForT); + } else if (initialSlope === 0.0) { + return guessForT; + } else { + return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize); + } + } -var define = { + var _precomputed = false; - animated: function animated() { - return function animatedImpl() { - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var cy = this._private.cy || this; + function precompute() { + _precomputed = true; + if (mX1 !== mY1 || mX2 !== mY2) { + calcSampleValues(); + } + } - if (!cy.styleEnabled()) { - return false; - } + var f = function f(aX) { + if (!_precomputed) { + precompute(); + } + if (mX1 === mY1 && mX2 === mY2) { + return aX; + } + if (aX === 0) { + return 0; + } + if (aX === 1) { + return 1; + } - var ele = all[0]; + return calcBezier(getTForX(aX), mY1, mY2); + }; - if (ele) { - return ele._private.animation.current.length > 0; - } - }; - }, // animated + f.getControlPoints = function () { + return [{ + x: mX1, + y: mY1 + }, { + x: mX2, + y: mY2 + }]; + }; - clearQueue: function clearQueue() { - return function clearQueueImpl() { - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var cy = this._private.cy || this; + var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")"; + f.toString = function () { + return str; + }; - if (!cy.styleEnabled()) { - return this; - } + return f; +} - for (var i = 0; i < all.length; i++) { - var ele = all[i]; - ele._private.animation.queue = []; - } +module.exports = generateCubicBezier; - return this; - }; - }, // clearQueue +/***/ }), +/* 75 */ +/***/ (function(module, exports, __webpack_require__) { - delay: function delay() { - return function delayImpl(time, complete) { - var cy = this._private.cy || this; +"use strict"; - if (!cy.styleEnabled()) { - return this; - } - return this.animate({ - delay: time, - duration: time, - complete: complete - }); +/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */ +/* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass + then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */ +var generateSpringRK4 = function () { + function springAccelerationForState(state) { + return -state.tension * state.x - state.friction * state.v; + } + + function springEvaluateStateWithDerivative(initialState, dt, derivative) { + var state = { + x: initialState.x + derivative.dx * dt, + v: initialState.v + derivative.dv * dt, + tension: initialState.tension, + friction: initialState.friction }; - }, // delay - delayAnimation: function delayAnimation() { - return function delayAnimationImpl(time, complete) { - var cy = this._private.cy || this; + return { dx: state.v, dv: springAccelerationForState(state) }; + } - if (!cy.styleEnabled()) { - return this; - } + function springIntegrateState(state, dt) { + var a = { + dx: state.v, + dv: springAccelerationForState(state) + }, + b = springEvaluateStateWithDerivative(state, dt * 0.5, a), + c = springEvaluateStateWithDerivative(state, dt * 0.5, b), + d = springEvaluateStateWithDerivative(state, dt, c), + dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx), + dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv); - return this.animation({ - delay: time, - duration: time, - complete: complete - }); - }; - }, // delay + state.x = state.x + dxdt * dt; + state.v = state.v + dvdt * dt; - animation: function animation() { - return function animationImpl(properties, params) { - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var cy = this._private.cy || this; - var isCore = !selfIsArrayLike; - var isEles = !isCore; + return state; + } - if (!cy.styleEnabled()) { - return this; - } + return function springRK4Factory(tension, friction, duration) { - var style = cy.style(); + var initState = { + x: -1, + v: 0, + tension: null, + friction: null + }, + path = [0], + time_lapsed = 0, + tolerance = 1 / 10000, + DT = 16 / 1000, + have_duration = void 0, + dt = void 0, + last_state = void 0; - properties = util.assign({}, properties, params); + tension = parseFloat(tension) || 500; + friction = parseFloat(friction) || 20; + duration = duration || null; - var propertiesEmpty = Object.keys(properties).length === 0; + initState.tension = tension; + initState.friction = friction; - if (propertiesEmpty) { - return new Animation(all[0], properties); // nothing to animate - } + have_duration = duration !== null; - if (properties.duration === undefined) { - properties.duration = 400; - } + /* Calculate the actual time it takes for this animation to complete with the provided conditions. */ + if (have_duration) { + /* Run the simulation without a duration. */ + time_lapsed = springRK4Factory(tension, friction); + /* Compute the adjusted time delta. */ + dt = time_lapsed / duration * DT; + } else { + dt = DT; + } - switch (properties.duration) { - case 'slow': - properties.duration = 600; - break; - case 'fast': - properties.duration = 200; - break; + for (;;) { + /* Next/step function .*/ + last_state = springIntegrateState(last_state || initState, dt); + /* Store the position. */ + path.push(1 + last_state.x); + time_lapsed += 16; + /* If the change threshold is reached, break. */ + if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) { + break; } + } - if (isEles) { - properties.style = style.getPropsList(properties.style || properties.css); - - properties.css = undefined; - } + /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the + computed path and returns a snapshot of the position according to a given percentComplete. */ + return !have_duration ? time_lapsed : function (percentComplete) { + return path[percentComplete * (path.length - 1) | 0]; + }; + }; +}(); - if (isEles && properties.renderedPosition != null) { - var rpos = properties.renderedPosition; - var pan = cy.pan(); - var zoom = cy.zoom(); +module.exports = generateSpringRK4; - properties.position = math.renderedToModelPosition(rpos, zoom, pan); - } +/***/ }), +/* 76 */ +/***/ (function(module, exports, __webpack_require__) { - // override pan w/ panBy if set - if (isCore && properties.panBy != null) { - var panBy = properties.panBy; - var cyPan = cy.pan(); +"use strict"; - properties.pan = { - x: cyPan.x + panBy.x, - y: cyPan.y + panBy.y - }; - } - // override pan w/ center if set - var center = properties.center || properties.centre; - if (isCore && center != null) { - var centerPan = cy.getCenterPan(center.eles, properties.zoom); +var is = __webpack_require__(0); - if (centerPan != null) { - properties.pan = centerPan; - } - } +function getEasedValue(type, start, end, percent, easingFn) { + if (percent === 1) { + return end; + } - // override pan & zoom w/ fit if set - if (isCore && properties.fit != null) { - var fit = properties.fit; - var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding); + var val = easingFn(start, end, percent); - if (fitVp != null) { - properties.pan = fitVp.pan; - properties.zoom = fitVp.zoom; - } - } + if (type == null) { + return val; + } - // override zoom (& potentially pan) w/ zoom obj if set - if (isCore && is.plainObject(properties.zoom)) { - var vp = cy.getZoomedViewport(properties.zoom); + if (type.roundValue || type.color) { + val = Math.round(val); + } - if (vp != null) { - if (vp.zoomed) { - properties.zoom = vp.zoom; - } + if (type.min !== undefined) { + val = Math.max(val, type.min); + } - if (vp.panned) { - properties.pan = vp.pan; - } - } - } + if (type.max !== undefined) { + val = Math.min(val, type.max); + } - return new Animation(all[0], properties); - }; - }, // animate + return val; +} - animate: function animate() { - return function animateImpl(properties, params) { - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var cy = this._private.cy || this; +function ease(startProp, endProp, percent, easingFn, propSpec) { + var type = propSpec != null ? propSpec.type : null; - if (!cy.styleEnabled()) { - return this; - } + if (percent < 0) { + percent = 0; + } else if (percent > 1) { + percent = 1; + } - if (params) { - properties = util.extend({}, properties, params); - } + var start = void 0, + end = void 0; - // manually hook and run the animation - for (var i = 0; i < all.length; i++) { - var ele = all[i]; - var queue = ele.animated() && (properties.queue === undefined || properties.queue); + if (startProp.pfValue != null || startProp.value != null) { + start = startProp.pfValue != null ? startProp.pfValue : startProp.value; + } else { + start = startProp; + } - var ani = ele.animation(properties, queue ? { queue: true } : undefined); + if (endProp.pfValue != null || endProp.value != null) { + end = endProp.pfValue != null ? endProp.pfValue : endProp.value; + } else { + end = endProp; + } - ani.play(); - } + if (is.number(start) && is.number(end)) { + return getEasedValue(type, start, end, percent, easingFn); + } else if (is.array(start) && is.array(end)) { + var easedArr = []; - return this; // chaining - }; - }, // animate + for (var i = 0; i < end.length; i++) { + var si = start[i]; + var ei = end[i]; - stop: function stop() { - return function stopImpl(clearQueue, jumpToEnd) { - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var cy = this._private.cy || this; + if (si != null && ei != null) { + var val = getEasedValue(type, si, ei, percent, easingFn); - if (!cy.styleEnabled()) { - return this; + easedArr.push(val); + } else { + easedArr.push(ei); } + } - for (var i = 0; i < all.length; i++) { - var ele = all[i]; - var _p = ele._private; - var anis = _p.animation.current; + return easedArr; + } - for (var j = 0; j < anis.length; j++) { - var ani = anis[j]; - var ani_p = ani._private; + return undefined; +} - if (jumpToEnd) { - // next iteration of the animation loop, the animation - // will go straight to the end and be removed - ani_p.duration = 0; - } - } +module.exports = ease; - // clear the queue of future animations - if (clearQueue) { - _p.animation.queue = []; - } +/***/ }), +/* 77 */ +/***/ (function(module, exports, __webpack_require__) { - if (!jumpToEnd) { - _p.animation.current = []; - } - } +"use strict"; - // we have to notify (the animation loop doesn't do it for us on `stop`) - cy.notify({ - eles: this, - type: 'draw' - }); - return this; +function startAnimation(self, ani, now, isCore) { + var isEles = !isCore; + var ele = self; + var ani_p = ani._private; + var cy = isCore ? self : self.cy(); + var style = cy.style(); + + if (isEles) { + var pos = ele.position(); + + ani_p.startPosition = ani_p.startPosition || { + x: pos.x, + y: pos.y }; - } // stop -}; // define + ani_p.startStyle = ani_p.startStyle || style.getAnimationStartStyle(ele, ani_p.style); + } -module.exports = define; + if (isCore) { + var pan = cy._private.pan; + + ani_p.startPan = ani_p.startPan || { + x: pan.x, + y: pan.y + }; + + ani_p.startZoom = ani_p.startZoom != null ? ani_p.startZoom : cy._private.zoom; + } + + ani_p.started = true; + ani_p.startTime = now - ani_p.progress * ani_p.duration; +} + +module.exports = startAnimation; /***/ }), -/* 73 */ +/* 78 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -var util = __webpack_require__(1); +var Emitter = __webpack_require__(11); +var define = __webpack_require__(4); var is = __webpack_require__(0); +var util = __webpack_require__(1); +var Selector = __webpack_require__(6); -var define = { +var emitterOptions = { + qualifierCompare: function qualifierCompare(selector1, selector2) { + if (selector1 == null || selector2 == null) { + return selector1 == null && selector2 == null; + } else { + return selector1.sameText(selector2); + } + }, + eventMatches: function eventMatches(cy, listener, eventObj) { + var selector = listener.qualifier; - // access data field - data: function data(params) { - var defaults = { - field: 'data', - bindingEvent: 'data', - allowBinding: false, - allowSetting: false, - allowGetting: false, - settingEvent: 'data', - settingTriggersEvent: false, - triggerFnName: 'trigger', - immutableKeys: {}, // key => true if immutable - updateStyle: false, - beforeGet: function beforeGet(self) {}, - beforeSet: function beforeSet(self, obj) {}, - onSet: function onSet(self) {}, - canSet: function canSet(self) { - return true; - } - }; - params = util.extend({}, defaults, params); + if (selector != null) { + return cy !== eventObj.target && is.element(eventObj.target) && selector.matches(eventObj.target); + } - return function dataImpl(name, value) { - var p = params; - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var single = selfIsArrayLike ? self[0] : self; - - // .data('foo', ...) - if (is.string(name)) { - // set or get property - - // .data('foo') - if (p.allowGetting && value === undefined) { - // get - - var ret = void 0; - if (single) { - p.beforeGet(single); - - ret = single._private[p.field][name]; - } - return ret; - - // .data('foo', 'bar') - } else if (p.allowSetting && value !== undefined) { - // set - var valid = !p.immutableKeys[name]; - if (valid) { - var change = _defineProperty({}, name, value); - - p.beforeSet(self, change); - - for (var i = 0, l = all.length; i < l; i++) { - var ele = all[i]; + return true; + }, + eventFields: function eventFields(cy) { + return { + cy: cy, + target: cy + }; + }, + callbackContext: function callbackContext(cy, listener, eventObj) { + return listener.qualifier != null ? eventObj.target : cy; + } +}; - if (p.canSet(ele)) { - ele._private[p.field][name] = value; - } - } +var argSelector = function argSelector(arg) { + if (is.string(arg)) { + return new Selector(arg); + } else { + return arg; + } +}; - // update mappers if asked - if (p.updateStyle) { - self.updateStyle(); - } +var elesfn = { + createEmitter: function createEmitter() { + var _p = this._private; - // call onSet callback - p.onSet(self); + if (!_p.emitter) { + _p.emitter = new Emitter(util.assign({ + context: this + }, emitterOptions)); + } - if (p.settingTriggersEvent) { - self[p.triggerFnName](p.settingEvent); - } - } - } + return this; + }, - // .data({ 'foo': 'bar' }) - } else if (p.allowSetting && is.plainObject(name)) { - // extend - var obj = name; - var k = void 0, - v = void 0; - var keys = Object.keys(obj); + emitter: function emitter() { + return this._private.emitter; + }, - p.beforeSet(self, obj); + on: function on(events, selector, callback) { + this.emitter().on(events, argSelector(selector), callback); - for (var _i = 0; _i < keys.length; _i++) { - k = keys[_i]; - v = obj[k]; + return this; + }, - var _valid = !p.immutableKeys[k]; - if (_valid) { - for (var j = 0; j < all.length; j++) { - var _ele = all[j]; + removeListener: function removeListener(events, selector, callback) { + this.emitter().removeListener(events, argSelector(selector), callback); - if (p.canSet(_ele)) { - _ele._private[p.field][k] = v; - } - } - } - } + return this; + }, - // update mappers if asked - if (p.updateStyle) { - self.updateStyle(); - } + one: function one(events, selector, callback) { + this.emitter().one(events, argSelector(selector), callback); - // call onSet callback - p.onSet(self); + return this; + }, - if (p.settingTriggersEvent) { - self[p.triggerFnName](p.settingEvent); - } + once: function once(events, selector, callback) { + this.emitter().one(events, argSelector(selector), callback); - // .data(function(){ ... }) - } else if (p.allowBinding && is.fn(name)) { - // bind to event - var fn = name; - self.on(p.bindingEvent, fn); + return this; + }, - // .data() - } else if (p.allowGetting && name === undefined) { - // get whole object - var _ret = void 0; - if (single) { - p.beforeGet(single); + emit: function emit(events, extraParams) { + this.emitter().emit(events, extraParams); - _ret = single._private[p.field]; - } - return _ret; - } + return this; + } +}; - return self; // maintain chainability - }; // function - }, // data +define.eventAliasesOn(elesfn); - // remove data field - removeData: function removeData(params) { - var defaults = { - field: 'data', - event: 'data', - triggerFnName: 'trigger', - triggerEvent: false, - immutableKeys: {} // key => true if immutable - }; - params = util.extend({}, defaults, params); +module.exports = elesfn; - return function removeDataImpl(names) { - var p = params; - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like +/***/ }), +/* 79 */ +/***/ (function(module, exports, __webpack_require__) { - // .removeData('foo bar') - if (is.string(names)) { - // then get the list of keys, and delete them - var keys = names.split(/\s+/); - var l = keys.length; +"use strict"; - for (var i = 0; i < l; i++) { - // delete each non-empty key - var key = keys[i]; - if (is.emptyString(key)) { - continue; - } - var valid = !p.immutableKeys[key]; // not valid if immutable - if (valid) { - for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) { - all[i_a]._private[p.field][key] = undefined; - } - } - } +var corefn = { - if (p.triggerEvent) { - self[p.triggerFnName](p.event); - } + png: function png(options) { + var renderer = this._private.renderer; + options = options || {}; - // .removeData() - } else if (names === undefined) { - // then delete all keys + return renderer.png(options); + }, - for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) { - var _privateFields = all[_i_a]._private[p.field]; - var _keys = Object.keys(_privateFields); + jpg: function jpg(options) { + var renderer = this._private.renderer; + options = options || {}; - for (var _i2 = 0; _i2 < _keys.length; _i2++) { - var _key = _keys[_i2]; - var validKeyToDelete = !p.immutableKeys[_key]; + options.bg = options.bg || '#fff'; - if (validKeyToDelete) { - _privateFields[_key] = undefined; - } - } - } + return renderer.jpg(options); + } - if (p.triggerEvent) { - self[p.triggerFnName](p.event); - } - } +}; - return self; // maintain chaining - }; // function - } // removeData -}; // define +corefn.jpeg = corefn.jpg; -module.exports = define; +module.exports = corefn; /***/ }), -/* 74 */ +/* 80 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Promise = __webpack_require__(5); +var util = __webpack_require__(1); +var is = __webpack_require__(0); -var define = { +var corefn = { - eventAliasesOn: function eventAliasesOn(proto) { - var p = proto; + layout: function layout(options) { + var cy = this; - p.addListener = p.listen = p.bind = p.on; - p.unlisten = p.unbind = p.off = p.removeListener; - p.trigger = p.emit; + if (options == null) { + util.error('Layout options must be specified to make a layout'); + return; + } - // this is just a wrapper alias of .on() - p.pon = p.promiseOn = function (events, selector) { - var self = this; - var args = Array.prototype.slice.call(arguments, 0); + if (options.name == null) { + util.error('A `name` must be specified to make a layout'); + return; + } - return new Promise(function (resolve, reject) { - var callback = function callback(e) { - self.off.apply(self, offArgs); - - resolve(e); - }; - - var onArgs = args.concat([callback]); - var offArgs = onArgs.concat([]); + var name = options.name; + var Layout = cy.extension('layout', name); - self.on.apply(self, onArgs); - }); - }; - } + if (Layout == null) { + util.error('Can not apply layout: No such layout `' + name + '` found; did you include its JS file?'); + return; + } -}; // define + var eles = void 0; + if (is.string(options.eles)) { + eles = cy.$(options.eles); + } else { + eles = options.eles != null ? options.eles : cy.$(); + } -module.exports = define; + var layout = new Layout(util.extend({}, options, { + cy: cy, + eles: eles + })); -/***/ }), -/* 75 */ -/***/ (function(module, exports, __webpack_require__) { + return layout; + } -"use strict"; +}; +corefn.createLayout = corefn.makeLayout = corefn.layout; -module.exports = [{ - type: 'layout', - extensions: __webpack_require__(81) -}, { - type: 'renderer', - extensions: __webpack_require__(114) -}]; +module.exports = corefn; /***/ }), -/* 76 */ +/* 81 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var util = __webpack_require__(1); -var math = __webpack_require__(2); -var is = __webpack_require__(0); +var corefn = { + notify: function notify(params) { + var _p = this._private; -var defaults = { - fit: true, // whether to fit the viewport to the graph - directed: false, // whether the tree is directed downwards (or edges can point in any direction if false) - padding: 30, // padding on fit - circle: false, // put depths in concentric circles if true, put depths top down if false - spacingFactor: 1.75, // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap) - boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } - avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space - nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm - roots: undefined, // the roots of the trees - maximalAdjustments: 0, // how many times to try to position the nodes in a maximal way (i.e. no backtracking) - animate: false, // whether to transition the node positions - animationDuration: 500, // duration of animation in ms if enabled - animationEasing: undefined, // easing of animation if enabled, - animateFilter: function animateFilter(node, i) { - return true; - }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts - ready: undefined, // callback on layoutready - stop: undefined, // callback on layoutstop - transform: function transform(node, position) { - return position; - } // transform a given node position. Useful for changing flow direction in discrete layouts -}; + if (_p.batchingNotify) { + var bEles = _p.batchNotifyEles; + var bTypes = _p.batchNotifyTypes; -function BreadthFirstLayout(options) { - this.options = util.extend({}, defaults, options); -} + if (params.eles) { + bEles.merge(params.eles); + } -BreadthFirstLayout.prototype.run = function () { - var params = this.options; - var options = params; + if (!bTypes.ids[params.type]) { + bTypes.push(params.type); + bTypes.ids[params.type] = true; + } - var cy = params.cy; - var eles = options.eles; - var nodes = eles.nodes().not(':parent'); - var graph = eles; + return; // notifications are disabled during batching + } - var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { - x1: 0, y1: 0, w: cy.width(), h: cy.height() - }); + if (!_p.notificationsEnabled) { + return; + } // exit on disabled - var roots = void 0; - if (is.elementOrCollection(options.roots)) { - roots = options.roots; - } else if (is.array(options.roots)) { - var rootsArray = []; + var renderer = this.renderer(); - for (var i = 0; i < options.roots.length; i++) { - var id = options.roots[i]; - var ele = cy.getElementById(id); - rootsArray.push(ele); + // exit if destroy() called on core or renderer in between frames #1499 #1528 + if (this.isDestroyed() || !renderer) { + return; } - roots = cy.collection(rootsArray); - } else if (is.string(options.roots)) { - roots = cy.$(options.roots); - } else { - if (options.directed) { - roots = nodes.roots(); - } else { - var components = []; - var unhandledNodes = nodes; - - var _loop = function _loop() { - var currComp = cy.collection(); + renderer.notify(params); + }, - eles.bfs({ - roots: unhandledNodes[0], - visit: function visit(node, edge, pNode, i, depth) { - currComp = currComp.add(node); - }, - directed: false - }); + notifications: function notifications(bool) { + var p = this._private; - unhandledNodes = unhandledNodes.not(currComp); - components.push(currComp); - }; + if (bool === undefined) { + return p.notificationsEnabled; + } else { + p.notificationsEnabled = bool ? true : false; + } + }, - while (unhandledNodes.length > 0) { - _loop(); - } + noNotifications: function noNotifications(callback) { + this.notifications(false); + callback(); + this.notifications(true); + }, - roots = cy.collection(); + batching: function batching() { + return this._private.batchCount > 0; + }, - var _loop2 = function _loop2(_i) { - var comp = components[_i]; - var maxDegree = comp.maxDegree(false); - var compRoots = comp.filter(function (ele) { - return ele.degree(false) === maxDegree; - }); + startBatch: function startBatch() { + var _p = this._private; - roots = roots.add(compRoots); - }; + if (_p.batchCount == null) { + _p.batchCount = 0; + } - for (var _i = 0; _i < components.length; _i++) { - _loop2(_i); - } + if (_p.batchCount === 0) { + _p.batchingStyle = _p.batchingNotify = true; + _p.batchStyleEles = this.collection(); + _p.batchNotifyEles = this.collection(); + _p.batchNotifyTypes = []; + _p.batchNotifyTypes.ids = {}; } - } - var depths = []; - var foundByBfs = {}; - var id2depth = {}; - var prevNode = {}; - var prevEdge = {}; - var successors = {}; + _p.batchCount++; - // find the depths of the nodes - graph.bfs({ - roots: roots, - directed: options.directed, - visit: function visit(node, edge, pNode, i, depth) { - var ele = node[0]; - var id = ele.id(); + return this; + }, - if (!depths[depth]) { - depths[depth] = []; - } + endBatch: function endBatch() { + var _p = this._private; - depths[depth].push(ele); - foundByBfs[id] = true; - id2depth[id] = depth; - prevNode[id] = pNode; - prevEdge[id] = edge; + _p.batchCount--; - if (pNode) { - var prevId = pNode.id(); - var succ = successors[prevId] = successors[prevId] || []; + if (_p.batchCount === 0) { + // update style for dirty eles + _p.batchingStyle = false; + _p.batchStyleEles.updateStyle(); - succ.push(node); - } + // notify the renderer of queued eles and event types + _p.batchingNotify = false; + this.notify({ + type: _p.batchNotifyTypes, + eles: _p.batchNotifyEles + }); } - }); - // check for nodes not found by bfs - var orphanNodes = []; - for (var _i2 = 0; _i2 < nodes.length; _i2++) { - var _ele = nodes[_i2]; + return this; + }, - if (foundByBfs[_ele.id()]) { - continue; - } else { - orphanNodes.push(_ele); - } - } + batch: function batch(callback) { + this.startBatch(); + callback(); + this.endBatch(); - // assign orphan nodes a depth from their neighborhood - var maxChecks = orphanNodes.length * 3; - var checks = 0; - while (orphanNodes.length !== 0 && checks < maxChecks) { - var node = orphanNodes.shift(); - var neighbors = node.neighborhood().nodes(); - var assignedDepth = false; + return this; + }, - for (var _i3 = 0; _i3 < neighbors.length; _i3++) { - var depth = id2depth[neighbors[_i3].id()]; + // for backwards compatibility + batchData: function batchData(map) { + var cy = this; - if (depth !== undefined) { - depths[depth].push(node); - assignedDepth = true; - break; - } - } + return this.batch(function () { + var ids = Object.keys(map); - if (!assignedDepth) { - orphanNodes.push(node); - } + for (var i = 0; i < ids.length; i++) { + var id = ids[i]; + var data = map[id]; + var ele = cy.getElementById(id); - checks++; + ele.data(data); + } + }); } +}; - // assign orphan nodes that are still left to the depth of their subgraph - while (orphanNodes.length !== 0) { - var _node = orphanNodes.shift(); - //let subgraph = graph.bfs( node ).path; - var _assignedDepth = false; +module.exports = corefn; - // for( let i = 0; i < subgraph.length; i++ ){ - // let depth = id2depth[ subgraph[i].id() ]; +/***/ }), +/* 82 */ +/***/ (function(module, exports, __webpack_require__) { - // if( depth !== undefined ){ - // depths[depth].push( node ); - // assignedDepth = true; - // break; - // } - // } +"use strict"; - if (!_assignedDepth) { - // worst case if the graph really isn't tree friendly, then just dump it in 0 - if (depths.length === 0) { - depths.push([]); - } - depths[0].push(_node); - } - } +var util = __webpack_require__(1); - // assign the nodes a depth and index - var assignDepthsToEles = function assignDepthsToEles() { - for (var _i4 = 0; _i4 < depths.length; _i4++) { - var _eles = depths[_i4]; +var corefn = { - for (var j = 0; j < _eles.length; j++) { - var _ele2 = _eles[j]; + renderTo: function renderTo(context, zoom, pan, pxRatio) { + var r = this._private.renderer; - if (_ele2 == null) { - _eles.splice(j, 1); - j--; - continue; - } + r.renderTo(context, zoom, pan, pxRatio); + return this; + }, - _ele2._private.scratch.breadthfirst = { - depth: _i4, - index: j - }; - } - } - }; - assignDepthsToEles(); + renderer: function renderer() { + return this._private.renderer; + }, - var intersectsDepth = function intersectsDepth(node) { - // returns true if has edges pointing in from a higher depth - var edges = node.connectedEdges(function (ele) { - return ele.data('target') === node.id(); + forceRender: function forceRender() { + this.notify({ + type: 'draw' }); - var thisInfo = node._private.scratch.breadthfirst; - var highestDepthOfOther = 0; - var highestOther = void 0; - for (var _i5 = 0; _i5 < edges.length; _i5++) { - var edge = edges[_i5]; - var otherNode = edge.source()[0]; - var otherInfo = otherNode._private.scratch.breadthfirst; - if (thisInfo.depth <= otherInfo.depth && highestDepthOfOther < otherInfo.depth) { - highestDepthOfOther = otherInfo.depth; - highestOther = otherNode; - } - } + return this; + }, - return highestOther; - }; + resize: function resize() { + this.invalidateSize(); - // make maximal if so set by adjusting depths - for (var adj = 0; adj < options.maximalAdjustments; adj++) { + this.notify({ + type: 'resize' + }); - var nDepths = depths.length; - var elesToMove = []; - for (var _i6 = 0; _i6 < nDepths; _i6++) { - var _depth = depths[_i6]; + this.emit('resize'); - var nDepth = _depth.length; - for (var j = 0; j < nDepth; j++) { - var _ele3 = _depth[j]; - var info = _ele3._private.scratch.breadthfirst; - var intEle = intersectsDepth(_ele3); + return this; + }, - if (intEle) { - info.intEle = intEle; - elesToMove.push(_ele3); - } - } + initRenderer: function initRenderer(options) { + var cy = this; + + var RendererProto = cy.extension('renderer', options.name); + if (RendererProto == null) { + util.error('Can not initialise: No such renderer `%s` found; did you include its JS file?', options.name); + return; } - for (var _i7 = 0; _i7 < elesToMove.length; _i7++) { - var _ele4 = elesToMove[_i7]; - var _info = _ele4._private.scratch.breadthfirst; - var _intEle = _info.intEle; - var intInfo = _intEle._private.scratch.breadthfirst; + cy._private.renderer = new RendererProto(util.extend({}, options, { cy: cy })); - depths[_info.depth][_info.index] = null; // remove from old depth & index (create hole to be cleaned) + this.notify({ type: 'init' }); + }, - // add to end of new depth - var newDepth = intInfo.depth + 1; - while (newDepth > depths.length - 1) { - depths.push([]); - } - depths[newDepth].push(_ele4); + destroyRenderer: function destroyRenderer() { + var cy = this; - _info.depth = newDepth; - _info.index = depths[newDepth].length - 1; + cy.notify({ type: 'destroy' }); // destroy the renderer + + var domEle = cy.container(); + if (domEle) { + domEle._cyreg = null; + + while (domEle.childNodes.length > 0) { + domEle.removeChild(domEle.childNodes[0]); + } } - assignDepthsToEles(); - } + cy._private.renderer = null; // to be extra safe, remove the ref + }, - // find min distance we need to leave between nodes - var minDistance = 0; - if (options.avoidOverlap) { - for (var _i8 = 0; _i8 < nodes.length; _i8++) { - var n = nodes[_i8]; - var nbb = n.layoutDimensions(options); - var w = nbb.w; - var h = nbb.h; + onRender: function onRender(fn) { + return this.on('render', fn); + }, - minDistance = Math.max(minDistance, w, h); - } + offRender: function offRender(fn) { + return this.off('render', fn); } - // get the weighted percent for an element based on its connectivity to other levels - var cachedWeightedPercent = {}; - var getWeightedPercent = function getWeightedPercent(ele) { - if (cachedWeightedPercent[ele.id()]) { - return cachedWeightedPercent[ele.id()]; - } +}; - var eleDepth = ele._private.scratch.breadthfirst.depth; - var neighbors = ele.neighborhood().nodes().not(':parent').intersection(nodes); - var percent = 0; - var samples = 0; +corefn.invalidateDimensions = corefn.resize; - for (var _i9 = 0; _i9 < neighbors.length; _i9++) { - var neighbor = neighbors[_i9]; - var bf = neighbor._private.scratch.breadthfirst; - var index = bf.index; - var _depth2 = bf.depth; - var _nDepth = depths[_depth2].length; +module.exports = corefn; - if (eleDepth > _depth2 || eleDepth === 0) { - // only get influenced by elements above - percent += index / _nDepth; - samples++; - } - } +/***/ }), +/* 83 */ +/***/ (function(module, exports, __webpack_require__) { - samples = Math.max(1, samples); - percent = percent / samples; +"use strict"; - if (samples === 0) { - // so lone nodes have a "don't care" state in sorting - percent = undefined; - } - cachedWeightedPercent[ele.id()] = percent; - return percent; - }; +var is = __webpack_require__(0); +var Collection = __webpack_require__(7); - // rearrange the indices in each depth level based on connectivity +var corefn = { - var sortFn = function sortFn(a, b) { - var apct = getWeightedPercent(a); - var bpct = getWeightedPercent(b); + // get a collection + // - empty collection on no args + // - collection of elements in the graph on selector arg + // - guarantee a returned collection when elements or collection specified + collection: function collection(eles, opts) { - return apct - bpct; - }; + if (is.string(eles)) { + return this.$(eles); + } else if (is.elementOrCollection(eles)) { + return eles.collection(); + } else if (is.array(eles)) { + return new Collection(this, eles, opts); + } - for (var times = 0; times < 3; times++) { - // do it a few times b/c the depths are dynamic and we want a more stable result + return new Collection(this); + }, - for (var _i10 = 0; _i10 < depths.length; _i10++) { - depths[_i10] = depths[_i10].sort(sortFn); + nodes: function nodes(selector) { + var nodes = this.$(function (ele) { + return ele.isNode(); + }); + + if (selector) { + return nodes.filter(selector); } - assignDepthsToEles(); // and update - } - var biggestDepthSize = 0; - for (var _i11 = 0; _i11 < depths.length; _i11++) { - biggestDepthSize = Math.max(depths[_i11].length, biggestDepthSize); - } + return nodes; + }, - var center = { - x: bb.x1 + bb.w / 2, - y: bb.x1 + bb.h / 2 - }; - - var getPosition = function getPosition(ele, isBottomDepth) { - var info = ele._private.scratch.breadthfirst; - var depth = info.depth; - var index = info.index; - var depthSize = depths[depth].length; - - var distanceX = Math.max(bb.w / (depthSize + 1), minDistance); - var distanceY = Math.max(bb.h / (depths.length + 1), minDistance); - var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length); - radiusStepSize = Math.max(radiusStepSize, minDistance); - - if (!options.circle) { + edges: function edges(selector) { + var edges = this.$(function (ele) { + return ele.isEdge(); + }); - var epos = { - x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX, - y: (depth + 1) * distanceY - }; + if (selector) { + return edges.filter(selector); + } - if (isBottomDepth) { - return epos; - } + return edges; + }, - // let succs = successors[ ele.id() ]; - // if( succs ){ - // epos.x = 0; - // - // for( let i = 0 ; i < succs.length; i++ ){ - // let spos = pos[ succs[i].id() ]; - // - // epos.x += spos.x; - // } - // - // epos.x /= succs.length; - // } else { - // //debugger; - // } + // search the graph like jQuery + $: function $(selector) { + var eles = this._private.elements; - return epos; + if (selector) { + return eles.filter(selector); } else { - if (options.circle) { - var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0); - var theta = 2 * Math.PI / depths[depth].length * index; - - if (depth === 0 && depths[0].length === 1) { - radius = 1; - } - - return { - x: center.x + radius * Math.cos(theta), - y: center.y + radius * Math.sin(theta) - }; - } else { - return { - x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX, - y: (depth + 1) * distanceY - }; - } + return eles.spawnSelf(); } - }; - - // get positions in reverse depth order - var pos = {}; - for (var _i12 = depths.length - 1; _i12 >= 0; _i12--) { - var _depth3 = depths[_i12]; - - for (var _j = 0; _j < _depth3.length; _j++) { - var _node2 = _depth3[_j]; + }, - pos[_node2.id()] = getPosition(_node2, _i12 === depths.length - 1); - } + mutableElements: function mutableElements() { + return this._private.elements; } - nodes.layoutPositions(this, options, function (node) { - return pos[node.id()]; - }); - - return this; // chaining }; -module.exports = BreadthFirstLayout; +// aliases +corefn.elements = corefn.filter = corefn.$; + +module.exports = corefn; /***/ }), -/* 77 */ +/* 84 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var util = __webpack_require__(1); -var math = __webpack_require__(2); var is = __webpack_require__(0); +var Style = __webpack_require__(18); -var defaults = { - fit: true, // whether to fit the viewport to the graph - padding: 30, // the padding on fit - boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } - avoidOverlap: true, // prevents node overlap, may overflow boundingBox and radius if not enough space - nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm - spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up - radius: undefined, // the radius of the circle - startAngle: 3 / 2 * Math.PI, // where nodes start in radians - sweep: undefined, // how many radians should be between the first and last node (defaults to full circle) - clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false) - sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') } - animate: false, // whether to transition the node positions - animationDuration: 500, // duration of animation in ms if enabled - animationEasing: undefined, // easing of animation if enabled - animateFilter: function animateFilter(node, i) { - return true; - }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts - ready: undefined, // callback on layoutready - stop: undefined, // callback on layoutstop - transform: function transform(node, position) { - return position; - } // transform a given node position. Useful for changing flow direction in discrete layouts - -}; +var corefn = { -function CircleLayout(options) { - this.options = util.extend({}, defaults, options); -} + style: function style(newStyle) { + if (newStyle) { + var s = this.setStyle(newStyle); -CircleLayout.prototype.run = function () { - var params = this.options; - var options = params; + s.update(); + } - var cy = params.cy; - var eles = options.eles; + return this._private.style; + }, - var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise; + setStyle: function setStyle(style) { + var _p = this._private; - var nodes = eles.nodes().not(':parent'); + if (is.stylesheet(style)) { + _p.style = style.generateStyle(this); + } else if (is.array(style)) { + _p.style = Style.fromJson(this, style); + } else if (is.string(style)) { + _p.style = Style.fromString(this, style); + } else { + _p.style = Style(this); + } - if (options.sort) { - nodes = nodes.sort(options.sort); + return _p.style; } +}; - var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { - x1: 0, y1: 0, w: cy.width(), h: cy.height() - }); +module.exports = corefn; - var center = { - x: bb.x1 + bb.w / 2, - y: bb.y1 + bb.h / 2 - }; +/***/ }), +/* 85 */ +/***/ (function(module, exports, __webpack_require__) { - var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep; - var dTheta = sweep / Math.max(1, nodes.length - 1); - var r = void 0; +"use strict"; - var minDistance = 0; - for (var i = 0; i < nodes.length; i++) { - var n = nodes[i]; - var nbb = n.layoutDimensions(options); - var w = nbb.w; - var h = nbb.h; - minDistance = Math.max(minDistance, w, h); - } +var util = __webpack_require__(1); +var is = __webpack_require__(0); +var Promise = __webpack_require__(5); - if (is.number(options.radius)) { - r = options.radius; - } else if (nodes.length <= 1) { - r = 0; - } else { - r = Math.min(bb.h, bb.w) / 2 - minDistance; - } +var styfn = {}; - // calculate the radius - if (nodes.length > 1 && options.avoidOverlap) { - // but only if more than one node (can't overlap) - minDistance *= 1.75; // just to have some nice spacing +// (potentially expensive calculation) +// apply the style to the element based on +// - its bypass +// - what selectors match it +styfn.apply = function (eles) { + var self = this; + var _p = self._private; + var cy = _p.cy; + var updatedEles = cy.collection(); - var dcos = Math.cos(dTheta) - Math.cos(0); - var dsin = Math.sin(dTheta) - Math.sin(0); - var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping - r = Math.max(rMin, r); + if (_p.newStyle) { + // clear style caches + _p.contextStyles = {}; + _p.propDiffs = {}; + + self.cleanElements(eles, true); } - var getPos = function getPos(ele, i) { - var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1); + for (var ie = 0; ie < eles.length; ie++) { + var ele = eles[ie]; - var rx = r * Math.cos(theta); - var ry = r * Math.sin(theta); - var pos = { - x: center.x + rx, - y: center.y + ry - }; + var cxtMeta = self.getContextMeta(ele); - return pos; - }; + if (cxtMeta.empty) { + continue; + } else { + updatedEles.merge(ele); + } - nodes.layoutPositions(this, options, getPos); + var cxtStyle = self.getContextStyle(cxtMeta); + var app = self.applyContextStyle(cxtMeta, cxtStyle, ele); - return this; // chaining -}; + if (!_p.newStyle) { + self.updateTransitions(ele, app.diffProps); + } -module.exports = CircleLayout; + self.updateStyleHints(ele); + } // for elements -/***/ }), -/* 78 */ -/***/ (function(module, exports, __webpack_require__) { + _p.newStyle = false; -"use strict"; + return updatedEles; +}; +styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) { + var self = this; + var cache = self._private.propDiffs = self._private.propDiffs || {}; + var dualCxtKey = oldCxtKey + '-' + newCxtKey; + var cachedVal = cache[dualCxtKey]; -var util = __webpack_require__(1); -var math = __webpack_require__(2); - -var defaults = { - fit: true, // whether to fit the viewport to the graph - padding: 30, // the padding on fit - startAngle: 3 / 2 * Math.PI, // where nodes start in radians - sweep: undefined, // how many radians should be between the first and last node (defaults to full circle) - clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false) - equidistant: false, // whether levels have an equal radial distance betwen them, may cause bounding box overflow - minNodeSpacing: 10, // min spacing between outside of nodes (used for radius adjustment) - boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } - avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space - nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm - height: undefined, // height of layout area (overrides container height) - width: undefined, // width of layout area (overrides container width) - spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up - concentric: function concentric(node) { - // returns numeric value for each node, placing higher nodes in levels towards the centre - return node.degree(); - }, - levelWidth: function levelWidth(nodes) { - // the letiation of concentric values in each level - return nodes.maxDegree() / 4; - }, - animate: false, // whether to transition the node positions - animationDuration: 500, // duration of animation in ms if enabled - animationEasing: undefined, // easing of animation if enabled - animateFilter: function animateFilter(node, i) { - return true; - }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts - ready: undefined, // callback on layoutready - stop: undefined, // callback on layoutstop - transform: function transform(node, position) { - return position; - } // transform a given node position. Useful for changing flow direction in discrete layouts -}; - -function ConcentricLayout(options) { - this.options = util.extend({}, defaults, options); -} + if (cachedVal) { + return cachedVal; + } -ConcentricLayout.prototype.run = function () { - var params = this.options; - var options = params; + var diffProps = []; + var addedProp = {}; - var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise; + for (var i = 0; i < self.length; i++) { + var cxt = self[i]; + var oldHasCxt = oldCxtKey[i] === 't'; + var newHasCxt = newCxtKey[i] === 't'; + var cxtHasDiffed = oldHasCxt !== newHasCxt; + var cxtHasMappedProps = cxt.mappedProperties.length > 0; - var cy = params.cy; + if (cxtHasDiffed || cxtHasMappedProps) { + var props = void 0; - var eles = options.eles; - var nodes = eles.nodes().not(':parent'); + if (cxtHasDiffed && cxtHasMappedProps) { + props = cxt.properties; // suffices b/c mappedProperties is a subset of properties + } else if (cxtHasDiffed) { + props = cxt.properties; // need to check them all + } else if (cxtHasMappedProps) { + props = cxt.mappedProperties; // only need to check mapped + } - var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { - x1: 0, y1: 0, w: cy.width(), h: cy.height() - }); + for (var j = 0; j < props.length; j++) { + var prop = props[j]; + var name = prop.name; - var center = { - x: bb.x1 + bb.w / 2, - y: bb.y1 + bb.h / 2 - }; + // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter + // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result + // is cached) + var laterCxtOverrides = false; + for (var k = i + 1; k < self.length; k++) { + var laterCxt = self[k]; + var hasLaterCxt = newCxtKey[k] === 't'; - var nodeValues = []; // { node, value } - var theta = options.startAngle; - var maxNodeSize = 0; + if (!hasLaterCxt) { + continue; + } // can't override unless the context is active - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var value = void 0; + laterCxtOverrides = laterCxt.properties[prop.name] != null; - // calculate the node value - value = options.concentric(node); - nodeValues.push({ - value: value, - node: node - }); + if (laterCxtOverrides) { + break; + } // exit early as long as one later context overrides + } - // for style mapping - node._private.scratch.concentric = value; - } + if (!addedProp[name] && !laterCxtOverrides) { + addedProp[name] = true; + diffProps.push(name); + } + } // for props + } // if + } // for contexts - // in case we used the `concentric` in style - nodes.updateStyle(); + cache[dualCxtKey] = diffProps; + return diffProps; +}; - // calculate max size now based on potentially updated mappers - for (var _i = 0; _i < nodes.length; _i++) { - var _node = nodes[_i]; - var nbb = _node.layoutDimensions(options); +styfn.getContextMeta = function (ele) { + var self = this; + var cxtKey = ''; + var diffProps = void 0; + var prevKey = ele._private.styleCxtKey || ''; - maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h); + if (self._private.newStyle) { + prevKey = ''; // since we need to apply all style if a fresh stylesheet } - // sort node values in descreasing order - nodeValues.sort(function (a, b) { - return b.value - a.value; - }); - - var levelWidth = options.levelWidth(nodes); - - // put the values into levels - var levels = [[]]; - var currentLevel = levels[0]; - for (var _i2 = 0; _i2 < nodeValues.length; _i2++) { - var val = nodeValues[_i2]; - - if (currentLevel.length > 0) { - var diff = Math.abs(currentLevel[0].value - val.value); + // get the cxt key + for (var i = 0; i < self.length; i++) { + var context = self[i]; + var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core' - if (diff >= levelWidth) { - currentLevel = []; - levels.push(currentLevel); - } + if (contextSelectorMatches) { + cxtKey += 't'; + } else { + cxtKey += 'f'; } + } // for context - currentLevel.push(val); - } + diffProps = self.getPropertiesDiff(prevKey, cxtKey); - // create positions from levels + ele._private.styleCxtKey = cxtKey; - var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes + return { + key: cxtKey, + diffPropNames: diffProps, + empty: diffProps.length === 0 + }; +}; - if (!options.avoidOverlap) { - // then strictly constrain to bb - var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1; - var maxR = Math.min(bb.w, bb.h) / 2 - minDist; - var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0); +// gets a computed ele style object based on matched contexts +styfn.getContextStyle = function (cxtMeta) { + var cxtKey = cxtMeta.key; + var self = this; + var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; - minDist = Math.min(minDist, rStep); + // if already computed style, returned cached copy + if (cxtStyles[cxtKey]) { + return cxtStyles[cxtKey]; } - // find the metrics for each level - var r = 0; - for (var _i3 = 0; _i3 < levels.length; _i3++) { - var level = levels[_i3]; - var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep; - var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); + var style = { + _private: { + key: cxtKey + } + }; - // calculate the radius - if (level.length > 1 && options.avoidOverlap) { - // but only if more than one node (can't overlap) - var dcos = Math.cos(dTheta) - Math.cos(0); - var dsin = Math.sin(dTheta) - Math.sin(0); - var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping + for (var i = 0; i < self.length; i++) { + var cxt = self[i]; + var hasCxt = cxtKey[i] === 't'; - r = Math.max(rMin, r); + if (!hasCxt) { + continue; } - level.r = r; + for (var j = 0; j < cxt.properties.length; j++) { + var prop = cxt.properties[j]; - r += minDist; + style[prop.name] = prop; + } } - if (options.equidistant) { - var rDeltaMax = 0; - var _r = 0; - - for (var _i4 = 0; _i4 < levels.length; _i4++) { - var _level = levels[_i4]; - var rDelta = _level.r - _r; + cxtStyles[cxtKey] = style; + return style; +}; - rDeltaMax = Math.max(rDeltaMax, rDelta); - } +styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) { + var self = this; + var diffProps = cxtMeta.diffPropNames; + var retDiffProps = {}; - _r = 0; - for (var _i5 = 0; _i5 < levels.length; _i5++) { - var _level2 = levels[_i5]; + for (var i = 0; i < diffProps.length; i++) { + var diffPropName = diffProps[i]; + var cxtProp = cxtStyle[diffPropName]; + var eleProp = ele.pstyle(diffPropName); - if (_i5 === 0) { - _r = _level2.r; + if (!cxtProp) { + // no context prop means delete + if (!eleProp) { + continue; // no existing prop means nothing needs to be removed + // nb affects initial application on mapped values like control-point-distances + } else if (eleProp.bypass) { + cxtProp = { name: diffPropName, deleteBypassed: true }; + } else { + cxtProp = { name: diffPropName, delete: true }; } + } - _level2.r = _r; - - _r += rDeltaMax; + // save cycles when the context prop doesn't need to be applied + if (eleProp === cxtProp) { + continue; } - } - // calculate the node positions - var pos = {}; // id => position - for (var _i6 = 0; _i6 < levels.length; _i6++) { - var _level3 = levels[_i6]; - var _dTheta = _level3.dTheta; - var _r2 = _level3.r; + var retDiffProp = retDiffProps[diffPropName] = { + prev: eleProp + }; - for (var j = 0; j < _level3.length; j++) { - var _val = _level3[j]; - var _theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j; + self.applyParsedProperty(ele, cxtProp); - var p = { - x: center.x + _r2 * Math.cos(_theta), - y: center.y + _r2 * Math.sin(_theta) - }; + retDiffProp.next = ele.pstyle(diffPropName); - pos[_val.node.id()] = p; + if (retDiffProp.next && retDiffProp.next.bypass) { + retDiffProp.next = retDiffProp.next.bypassed; } } - // position the nodes - nodes.layoutPositions(this, options, function (ele) { - var id = ele.id(); - - return pos[id]; - }); - - return this; // chaining + return { + diffProps: retDiffProps + }; }; -module.exports = ConcentricLayout; +styfn.updateStyleHints = function (ele) { + var _p = ele._private; + var self = this; -/***/ }), -/* 79 */ -/***/ (function(module, exports, __webpack_require__) { + if (ele.removed()) { + return; + } -"use strict"; + // set whether has pie or not; for greater efficiency + var hasPie = false; + if (_p.group === 'nodes') { + for (var i = 1; i <= self.pieBackgroundN; i++) { + // 1..N + var _size = ele.pstyle('pie-' + i + '-background-size').value; + if (_size > 0) { + hasPie = true; + break; + } + } + } -/* -The CoSE layout was written by Gerardo Huck. -https://www.linkedin.com/in/gerardohuck/ + _p.hasPie = hasPie; -Based on the following article: -http://dl.acm.org/citation.cfm?id=1498047 + var transform = ele.pstyle('text-transform').strValue; + var content = ele.pstyle('label').strValue; + var srcContent = ele.pstyle('source-label').strValue; + var tgtContent = ele.pstyle('target-label').strValue; + var fStyle = ele.pstyle('font-style').strValue; + var size = ele.pstyle('font-size').pfValue + 'px'; + var family = ele.pstyle('font-family').strValue; + // let letiant = style['font-letiant'].strValue; + var weight = ele.pstyle('font-weight').strValue; + var valign = ele.pstyle('text-valign').strValue; + var halign = ele.pstyle('text-valign').strValue; + var oWidth = ele.pstyle('text-outline-width').pfValue; + var wrap = ele.pstyle('text-wrap').strValue; + var wrapW = ele.pstyle('text-max-width').pfValue; + var labelStyleKey = fStyle + '$' + size + '$' + family + '$' + weight + '$' + transform + '$' + valign + '$' + halign + '$' + oWidth + '$' + wrap + '$' + wrapW; + _p.labelStyleKey = labelStyleKey; + _p.sourceLabelKey = labelStyleKey + '$' + srcContent; + _p.targetLabelKey = labelStyleKey + '$' + tgtContent; + _p.labelKey = labelStyleKey + '$' + content; + _p.fontKey = fStyle + '$' + weight + '$' + size + '$' + family; -Modifications tracked on Github. -*/ + _p.styleKey = Date.now(); +}; -var util = __webpack_require__(1); -var math = __webpack_require__(2); -var is = __webpack_require__(0); -var Promise = __webpack_require__(5); +// apply a property to the style (for internal use) +// returns whether application was successful +// +// now, this function flattens the property, and here's how: +// +// for parsedProp:{ bypass: true, deleteBypass: true } +// no property is generated, instead the bypass property in the +// element's style is replaced by what's pointed to by the `bypassed` +// field in the bypass property (i.e. restoring the property the +// bypass was overriding) +// +// for parsedProp:{ mapped: truthy } +// the generated flattenedProp:{ mapping: prop } +// +// for parsedProp:{ bypass: true } +// the generated flattenedProp:{ bypassed: parsedProp } +styfn.applyParsedProperty = function (ele, parsedProp) { + var self = this; + var prop = parsedProp; + var style = ele._private.style; + var fieldVal = void 0, + flatProp = void 0; + var types = self.types; + var type = self.properties[prop.name].type; + var propIsBypass = prop.bypass; + var origProp = style[prop.name]; + var origPropIsBypass = origProp && origProp.bypass; + var _p = ele._private; + var flatPropMapping = 'mapping'; -var DEBUG; + var checkZOrder = function checkZOrder() { + self.checkZOrderTrigger(ele, prop.name, origProp ? origProp.value : null, prop.value); + }; -/** - * @brief : default layout options - */ -var defaults = { - // Called on `layoutready` - ready: function ready() {}, + // edges connected to compound nodes can not be haystacks + if (parsedProp.name === 'curve-style' && parsedProp.value === 'haystack' && ele.isEdge() && (ele.isLoop() || ele.source().isParent() || ele.target().isParent())) { + prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass); + } - // Called on `layoutstop` - stop: function stop() {}, + if (prop.delete) { + // delete the property and use the default value on falsey value + style[prop.name] = undefined; - // Whether to animate while running the layout - // true : Animate continuously as the layout is running - // false : Just show the end result - // 'end' : Animate with the end result, from the initial positions to the end positions - animate: true, + checkZOrder(); - // Easing of the animation for animate:'end' - animationEasing: undefined, + return true; + } - // The duration of the animation for animate:'end' - animationDuration: undefined, + if (prop.deleteBypassed) { + // delete the property that the + if (!origProp) { + checkZOrder(); - // A function that determines whether the node should be animated - // All nodes animated by default on animate enabled - // Non-animated nodes are positioned immediately when the layout starts - animateFilter: function animateFilter(node, i) { - return true; - }, + return true; // can't delete if no prop + } else if (origProp.bypass) { + // delete bypassed + origProp.bypassed = undefined; - // The layout animates only after this many milliseconds for animate:true - // (prevents flashing on fast runs) - animationThreshold: 250, + checkZOrder(); - // Number of iterations between consecutive screen positions update - // (0 -> only updated on the end) - refresh: 20, + return true; + } else { + return false; // we're unsuccessful deleting the bypassed + } + } - // Whether to fit the network view after when done - fit: true, + // check if we need to delete the current bypass + if (prop.deleteBypass) { + // then this property is just here to indicate we need to delete + if (!origProp) { + checkZOrder(); - // Padding on fit - padding: 30, + return true; // property is already not defined + } else if (origProp.bypass) { + // then replace the bypass property with the original + // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary) + style[prop.name] = origProp.bypassed; - // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } - boundingBox: undefined, + checkZOrder(); - // Excludes the label when calculating node bounding boxes for the layout algorithm - nodeDimensionsIncludeLabels: false, + return true; + } else { + return false; // we're unsuccessful deleting the bypass + } + } - // Randomize the initial positions of the nodes (true) or use existing positions (false) - randomize: false, + var printMappingErr = function printMappingErr() { + util.error('Do not assign mappings to elements without corresponding data (e.g. ele `' + ele.id() + '` for property `' + prop.name + '` with data field `' + prop.field + '`); try a `[' + prop.field + ']` selector to limit scope to elements with `' + prop.field + '` defined'); + }; - // Extra spacing between components in non-compound graphs - componentSpacing: 40, + // put the property in the style objects + switch (prop.mapped) {// flatten the property if mapped + case types.mapData: + { + // flatten the field (e.g. data.foo.bar) + var fields = prop.field.split('.'); + var _fieldVal = _p.data; - // Node repulsion (non overlapping) multiplier - nodeRepulsion: function nodeRepulsion(node) { - return 2048; - }, + for (var i = 0; i < fields.length && _fieldVal; i++) { + var field = fields[i]; + _fieldVal = _fieldVal[field]; + } - // Node repulsion (overlapping) multiplier - nodeOverlap: 4, + var percent = void 0; + if (!is.number(_fieldVal)) { + // then keep the mapping but assume 0% for now + percent = 0; + } else { + percent = (_fieldVal - prop.fieldMin) / (prop.fieldMax - prop.fieldMin); + } - // Ideal edge (non nested) length - idealEdgeLength: function idealEdgeLength(edge) { - return 32; - }, + // make sure to bound percent value + if (percent < 0) { + percent = 0; + } else if (percent > 1) { + percent = 1; + } - // Divisor to compute edge forces - edgeElasticity: function edgeElasticity(edge) { - return 32; - }, + if (type.color) { + var r1 = prop.valueMin[0]; + var r2 = prop.valueMax[0]; + var g1 = prop.valueMin[1]; + var g2 = prop.valueMax[1]; + var b1 = prop.valueMin[2]; + var b2 = prop.valueMax[2]; + var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3]; + var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3]; - // Nesting factor (multiplier) to compute ideal edge length for nested edges - nestingFactor: 1.2, + var clr = [Math.round(r1 + (r2 - r1) * percent), Math.round(g1 + (g2 - g1) * percent), Math.round(b1 + (b2 - b1) * percent), Math.round(a1 + (a2 - a1) * percent)]; - // Gravity force (constant) - gravity: 1, + flatProp = { // colours are simple, so just create the flat property instead of expensive string parsing + bypass: prop.bypass, // we're a bypass if the mapping property is a bypass + name: prop.name, + value: clr, + strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')' + }; + } else if (type.number) { + var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent; + flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping); + } else { + return false; // can only map to colours and numbers + } - // Maximum number of iterations to perform - numIter: 1000, + if (!flatProp) { + // if we can't flatten the property, then use the origProp so we still keep the mapping itself + flatProp = this.parse(prop.name, origProp.strValue, prop.bypass, flatPropMapping); + } - // Initial temperature (maximum node displacement) - initialTemp: 1000, + if (!flatProp) { + printMappingErr(); + } + flatProp.mapping = prop; // keep a reference to the mapping + prop = flatProp; // the flattened (mapped) property is the one we want - // Cooling factor (how the temperature is reduced between consecutive iterations - coolingFactor: 0.99, + break; + } - // Lower temperature threshold (below this point the layout will end) - minTemp: 1.0, + // direct mapping + case types.data: + { + // flatten the field (e.g. data.foo.bar) + var _fields = prop.field.split('.'); + var _fieldVal2 = _p.data; - // Pass a reference to weaver to use threads for calculations - weaver: false -}; + if (_fieldVal2) { + for (var _i = 0; _i < _fields.length; _i++) { + var _field = _fields[_i]; + _fieldVal2 = _fieldVal2[_field]; + } + } -/** - * @brief : constructor - * @arg options : object containing layout options - */ -function CoseLayout(options) { - this.options = util.extend({}, defaults, options); + flatProp = this.parse(prop.name, _fieldVal2, prop.bypass, flatPropMapping); - this.options.layout = this; -} + if (!flatProp) { + // if we can't flatten the property, then use the origProp so we still keep the mapping itself + var flatPropVal = origProp ? origProp.strValue : ''; -/** - * @brief : runs the layout - */ -CoseLayout.prototype.run = function () { - var options = this.options; - var cy = options.cy; - var layout = this; - var thread = this.thread; - var Thread = options.weaver ? options.weaver.Thread : null; + flatProp = this.parse(prop.name, flatPropVal, prop.bypass, flatPropMapping); + } - var falseThread = { // use false thread as polyfill - listeners: [], - on: function on(e, cb) { - this.listeners.push({ event: e, callback: cb }); + if (!flatProp) { + printMappingErr(); + } + flatProp.mapping = prop; // keep a reference to the mapping + prop = flatProp; // the flattened (mapped) property is the one we want - return this; - }, - trigger: function trigger(e) { - if (is.string(e)) { - e = { type: e }; + break; } - var matchesEvent = function matchesEvent(l) { - return l.event === e.type; - }; - var trigger = function trigger(l) { - l.callback(e); - }; + case types.fn: + { + var fn = prop.value; + var fnRetVal = fn(ele); - this.listeners.filter(matchesEvent).forEach(trigger); + flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping); + flatProp.mapping = prop; // keep a reference to the mapping + prop = flatProp; // the flattened (mapped) property is the one we want - return this; - }, - pass: function pass(data) { - this.pass = data; + break; + } - return this; - }, - run: function run(cb) { - var pass = this.pass; + case undefined: + break; // just set the property - return new Promise(function (resolve) { - resolve(cb(pass)); - }); - }, - stop: function stop() { - return this; - } - }; + default: + return false; // not a valid mapping + } - function broadcast(message) { - // for false thread - var e = { type: 'message', message: message }; + // if the property is a bypass property, then link the resultant property to the original one + if (propIsBypass) { + if (origPropIsBypass) { + // then this bypass overrides the existing one + prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass + } else { + // then link the orig prop to the new bypass + prop.bypassed = origProp; + } - falseThread.trigger(e); + style[prop.name] = prop; // and set + } else { + // prop is not bypass + if (origPropIsBypass) { + // then keep the orig prop (since it's a bypass) and link to the new prop + origProp.bypassed = prop; + } else { + // then just replace the old prop with the new one + style[prop.name] = prop; + } } - if (!thread || thread.stopped()) { - thread = this.thread = Thread ? new Thread() : falseThread; - } + checkZOrder(); - layout.stopped = false; + return true; +}; - if (options.animate === true || options.animate === false) { - layout.emit({ type: 'layoutstart', layout: layout }); - } +styfn.cleanElements = function (eles, keepBypasses) { + var self = this; + var props = self.properties; - // Set DEBUG - Global variable - if (true === options.debug) { - DEBUG = true; - } else { - DEBUG = false; - } + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - // Initialize layout info - var layoutInfo = createLayoutInfo(cy, layout, options); + if (!keepBypasses) { + ele._private.style = {}; + } else { + var style = ele._private.style; - // Show LayoutInfo contents if debugging - if (DEBUG) { - printLayoutInfo(layoutInfo); - } + for (var j = 0; j < props.length; j++) { + var prop = props[j]; + var eleProp = style[prop.name]; - // If required, randomize node positions - if (options.randomize) { - randomizePositions(layoutInfo, cy); + if (eleProp) { + if (eleProp.bypass) { + eleProp.bypassed = null; + } else { + style[prop.name] = null; + } + } + } + } } +}; - var startTime = Date.now(); - var refreshRequested = false; - var refresh = function refresh(rOpts) { - rOpts = rOpts || {}; +// updates the visual style for all elements (useful for manual style modification after init) +styfn.update = function () { + var cy = this._private.cy; + var eles = cy.mutableElements(); - if (refreshRequested && !rOpts.next) { - return; - } + eles.updateStyle(); +}; - if (!rOpts.force && Date.now() - startTime < options.animationThreshold) { - return; - } +// just update the functional properties (i.e. mappings) in the elements' +// styles (less expensive than recalculation) +styfn.updateMappers = function (eles) { + var self = this; + var cy = this._private.cy; + var updatedEles = cy.collection(); - refreshRequested = true; + for (var i = 0; i < eles.length; i++) { + // for each ele + var ele = eles[i]; + var style = ele._private.style; + var updatedEle = false; - util.requestAnimationFrame(function () { - refreshPositions(layoutInfo, cy, options); + for (var j = 0; j < self.properties.length; j++) { + // for each prop + var prop = self.properties[j]; + var propInStyle = style[prop.name]; - // Fit the graph if necessary - if (true === options.fit) { - cy.fit(options.padding); - } + if (propInStyle && propInStyle.mapping) { + var mapping = propInStyle.mapping; - refreshRequested = false; + this.applyParsedProperty(ele, mapping); // reapply the mapping property - if (rOpts.next) { - rOpts.next(); + updatedEle = true; } - }); - }; + } - thread.on('message', function (e) { - var layoutNodes = e.message; + if (updatedEle) { + this.updateStyleHints(ele); - layoutInfo.layoutNodes = layoutNodes; - refresh(); - }); + updatedEles.merge(ele); + } + } - thread.pass({ - layoutInfo: layoutInfo, - options: { - animate: options.animate, - refresh: options.refresh, - componentSpacing: options.componentSpacing, - nodeOverlap: options.nodeOverlap, - nestingFactor: options.nestingFactor, - gravity: options.gravity, - numIter: options.numIter, - initialTemp: options.initialTemp, - coolingFactor: options.coolingFactor, - minTemp: options.minTemp - } - }).run(function (pass) { - var layoutInfo = pass.layoutInfo; - var options = pass.options; - var stopped = false; + return updatedEles; +}; - /** - * @brief : Performs one iteration of the physical simulation - * @arg layoutInfo : LayoutInfo object already initialized - * @arg cy : Cytoscape object - * @arg options : Layout options - */ - var step = function step(layoutInfo, options, _step) { - // var s = "\n\n###############################"; - // s += "\nSTEP: " + step; - // s += "\n###############################\n"; - // logDebug(s); +// diffProps : { name => { prev, next } } +styfn.updateTransitions = function (ele, diffProps, isBypass) { + var self = this; + var _p = ele._private; + var props = ele.pstyle('transition-property').value; + var duration = ele.pstyle('transition-duration').pfValue; + var delay = ele.pstyle('transition-delay').pfValue; - // Calculate node repulsions - calculateNodeForces(layoutInfo, options); - // Calculate edge forces - calculateEdgeForces(layoutInfo, options); - // Calculate gravity forces - calculateGravityForces(layoutInfo, options); - // Propagate forces from parent to child - propagateForces(layoutInfo, options); - // Update positions based on calculated forces - updatePositions(layoutInfo, options); - }; + if (props.length > 0 && duration > 0) { - /** - * @brief : Computes the node repulsion forces - */ - var calculateNodeForces = function calculateNodeForces(layoutInfo, options) { - // Go through each of the graphs in graphSet - // Nodes only repel each other if they belong to the same graph - // var s = 'calculateNodeForces'; - // logDebug(s); - for (var i = 0; i < layoutInfo.graphSet.length; i++) { - var graph = layoutInfo.graphSet[i]; - var numNodes = graph.length; + var style = {}; - // s = "Set: " + graph.toString(); - // logDebug(s); + // build up the style to animate towards + var anyPrev = false; + for (var i = 0; i < props.length; i++) { + var prop = props[i]; + var styProp = ele.pstyle(prop); + var diffProp = diffProps[prop]; - // Now get all the pairs of nodes - // Only get each pair once, (A, B) = (B, A) - for (var j = 0; j < numNodes; j++) { - var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; + if (!diffProp) { + continue; + } - for (var k = j + 1; k < numNodes; k++) { - var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]]; + var prevProp = diffProp.prev; + var fromProp = prevProp; + var toProp = diffProp.next != null ? diffProp.next : styProp; + var diff = false; + var initVal = void 0; + var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity) - nodeRepulsion(node1, node2, layoutInfo, options); - } - } + if (!fromProp) { + continue; } - }; - var randomDistance = function randomDistance(max) { - return -max + 2 * max * Math.random(); - }; + // consider px values + if (is.number(fromProp.pfValue) && is.number(toProp.pfValue)) { + diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy + initVal = fromProp.pfValue + initDt * diff; - /** - * @brief : Compute the node repulsion forces between a pair of nodes - */ - var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) { - // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id; + // consider numerical values + } else if (is.number(fromProp.value) && is.number(toProp.value)) { + diff = toProp.value - fromProp.value; // nonzero is truthy + initVal = fromProp.value + initDt * diff; - var cmptId1 = node1.cmptId; - var cmptId2 = node2.cmptId; + // consider colour values + } else if (is.array(fromProp.value) && is.array(toProp.value)) { + diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2]; - if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) { - return; + initVal = fromProp.strValue; } - // Get direction of line connecting both node centers - var directionX = node2.positionX - node1.positionX; - var directionY = node2.positionY - node1.positionY; - var maxRandDist = 1; - // s += "\ndirectionX: " + directionX + ", directionY: " + directionY; - - // If both centers are the same, apply a random force - if (0 === directionX && 0 === directionY) { - directionX = randomDistance(maxRandDist); - directionY = randomDistance(maxRandDist); + // the previous value is good for an animation only if it's different + if (diff) { + style[prop] = toProp.strValue; // to val + this.applyBypass(ele, prop, initVal); // from val + anyPrev = true; } + } // end if props allow ani - var overlap = nodesOverlap(node1, node2, directionX, directionY); + // can't transition if there's nothing previous to transition from + if (!anyPrev) { + return; + } - if (overlap > 0) { - // s += "\nNodes DO overlap."; - // s += "\nOverlap: " + overlap; - // If nodes overlap, repulsion force is proportional - // to the overlap - var force = options.nodeOverlap * overlap; + _p.transitioning = true; - // Compute the module and components of the force vector - var distance = Math.sqrt(directionX * directionX + directionY * directionY); - // s += "\nDistance: " + distance; - var forceX = force * directionX / distance; - var forceY = force * directionY / distance; + new Promise(function (resolve) { + if (delay > 0) { + ele.delayAnimation(delay).play().promise().then(resolve); } else { - // s += "\nNodes do NOT overlap."; - // If there's no overlap, force is inversely proportional - // to squared distance + resolve(); + } + }).then(function () { + return ele.animation({ + style: style, + duration: duration, + easing: ele.pstyle('transition-timing-function').value, + queue: false + }).play().promise(); + }).then(function () { + // if( !isBypass ){ + self.removeBypasses(ele, props); + ele.emitAndNotify('style'); + // } - // Get clipping points for both nodes - var point1 = findClippingPoint(node1, directionX, directionY); - var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); + _p.transitioning = false; + }); + } else if (_p.transitioning) { + this.removeBypasses(ele, props); + ele.emitAndNotify('style'); - // Use clipping points to compute distance - var distanceX = point2.x - point1.x; - var distanceY = point2.y - point1.y; - var distanceSqr = distanceX * distanceX + distanceY * distanceY; - var distance = Math.sqrt(distanceSqr); - // s += "\nDistance: " + distance; + _p.transitioning = false; + } +}; - // Compute the module and components of the force vector - var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr; - var forceX = force * distanceX / distance; - var forceY = force * distanceY / distance; - } +styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) { + var prop = this.properties[name]; - // Apply force - if (!node1.isLocked) { - node1.offsetX -= forceX; - node1.offsetY -= forceY; - } + if (prop.triggersZOrder != null && (fromValue == null || prop.triggersZOrder(fromValue, toValue))) { + this._private.cy.notify({ + type: 'zorder', + eles: ele + }); + } +}; - if (!node2.isLocked) { - node2.offsetX += forceX; - node2.offsetY += forceY; - } +module.exports = styfn; - // s += "\nForceX: " + forceX + " ForceY: " + forceY; - // logDebug(s); +/***/ }), +/* 86 */ +/***/ (function(module, exports, __webpack_require__) { - return; - }; +"use strict"; - /** - * @brief : Determines whether two nodes overlap or not - * @return : Amount of overlapping (0 => no overlap) - */ - var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) { - if (dX > 0) { - var overlapX = node1.maxX - node2.minX; - } else { - var overlapX = node2.maxX - node1.minX; - } +var is = __webpack_require__(0); +var util = __webpack_require__(1); - if (dY > 0) { - var overlapY = node1.maxY - node2.minY; - } else { - var overlapY = node2.maxY - node1.minY; - } +var styfn = {}; - if (overlapX >= 0 && overlapY >= 0) { - return Math.sqrt(overlapX * overlapX + overlapY * overlapY); - } else { - return 0; - } - }; +// bypasses are applied to an existing style on an element, and just tacked on temporarily +// returns true iff application was successful for at least 1 specified property +styfn.applyBypass = function (eles, name, value, updateTransitions) { + var self = this; + var props = []; + var isBypass = true; - /** - * @brief : Finds the point in which an edge (direction dX, dY) intersects - * the rectangular bounding box of it's source/target node - */ - var findClippingPoint = function findClippingPoint(node, dX, dY) { + // put all the properties (can specify one or many) in an array after parsing them + if (name === '*' || name === '**') { + // apply to all property names - // Shorcuts - var X = node.positionX; - var Y = node.positionY; - var H = node.height || 1; - var W = node.width || 1; - var dirSlope = dY / dX; - var nodeSlope = H / W; - - // var s = 'Computing clipping point of node ' + node.id + - // " . Height: " + H + ", Width: " + W + - // "\nDirection " + dX + ", " + dY; - // - // Compute intersection - var res = {}; + if (value !== undefined) { + for (var i = 0; i < self.properties.length; i++) { + var prop = self.properties[i]; + var _name = prop.name; - // Case: Vertical direction (up) - if (0 === dX && 0 < dY) { - res.x = X; - // s += "\nUp direction"; - res.y = Y + H / 2; + var parsedProp = this.parse(_name, value, true); - return res; + if (parsedProp) { + props.push(parsedProp); + } } + } + } else if (is.string(name)) { + // then parse the single property + var _parsedProp = this.parse(name, value, true); - // Case: Vertical direction (down) - if (0 === dX && 0 > dY) { - res.x = X; - res.y = Y + H / 2; - // s += "\nDown direction"; + if (_parsedProp) { + props.push(_parsedProp); + } + } else if (is.plainObject(name)) { + // then parse each property + var specifiedProps = name; + updateTransitions = value; - return res; - } + var names = Object.keys(specifiedProps); - // Case: Intersects the right border - if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) { - res.x = X + W / 2; - res.y = Y + W * dY / 2 / dX; - // s += "\nRightborder"; + for (var _i = 0; _i < names.length; _i++) { + var _name2 = names[_i]; + var _prop = self.properties[_name2]; + var _value = specifiedProps[_name2]; - return res; + if (_value === undefined) { + // try camel case name too + _value = specifiedProps[util.dash2camel(_name2)]; } - // Case: Intersects the left border - if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) { - res.x = X - W / 2; - res.y = Y - W * dY / 2 / dX; - // s += "\nLeftborder"; + if (_value !== undefined) { + var _parsedProp2 = this.parse(_name2, _value, true); - return res; + if (_parsedProp2) { + props.push(_parsedProp2); + } } + } + } else { + // can't do anything without well defined properties + return false; + } - // Case: Intersects the top border - if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) { - res.x = X + H * dX / 2 / dY; - res.y = Y + H / 2; - // s += "\nTop border"; + // we've failed if there are no valid properties + if (props.length === 0) { + return false; + } - return res; - } + // now, apply the bypass properties on the elements + var ret = false; // return true if at least one succesful bypass applied + for (var _i2 = 0; _i2 < eles.length; _i2++) { + // for each ele + var ele = eles[_i2]; + var diffProps = {}; + var diffProp = void 0; - // Case: Intersects the bottom border - if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) { - res.x = X - H * dX / 2 / dY; - res.y = Y - H / 2; - // s += "\nBottom border"; + for (var j = 0; j < props.length; j++) { + // for each prop + var _prop2 = props[j]; - return res; + if (updateTransitions) { + var prevProp = ele.pstyle(_prop2.name); + diffProp = diffProps[_prop2.name] = { prev: prevProp }; } - // s += "\nClipping point found at " + res.x + ", " + res.y; - // logDebug(s); - return res; - }; - - /** - * @brief : Calculates all edge forces - */ - var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) { - // Iterate over all edges - for (var i = 0; i < layoutInfo.edgeSize; i++) { - // Get edge, source & target nodes - var edge = layoutInfo.layoutEdges[i]; - var sourceIx = layoutInfo.idToIndex[edge.sourceId]; - var source = layoutInfo.layoutNodes[sourceIx]; - var targetIx = layoutInfo.idToIndex[edge.targetId]; - var target = layoutInfo.layoutNodes[targetIx]; + ret = this.applyParsedProperty(ele, _prop2) || ret; - // Get direction of line connecting both node centers - var directionX = target.positionX - source.positionX; - var directionY = target.positionY - source.positionY; + if (updateTransitions) { + diffProp.next = ele.pstyle(_prop2.name); + } + } // for props - // If both centers are the same, do nothing. - // A random force has already been applied as node repulsion - if (0 === directionX && 0 === directionY) { - continue; - } + if (ret) { + this.updateStyleHints(ele); + } - // Get clipping points for both nodes - var point1 = findClippingPoint(source, directionX, directionY); - var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY); + if (updateTransitions) { + this.updateTransitions(ele, diffProps, isBypass); + } + } // for eles - var lx = point2.x - point1.x; - var ly = point2.y - point1.y; - var l = Math.sqrt(lx * lx + ly * ly); + return ret; +}; - var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity; +// only useful in specific cases like animation +styfn.overrideBypass = function (eles, name, value) { + name = util.camel2dash(name); - if (0 !== l) { - var forceX = force * lx / l; - var forceY = force * ly / l; - } else { - var forceX = 0; - var forceY = 0; - } + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var prop = ele._private.style[name]; + var type = this.properties[name].type; + var isColor = type.color; + var isMulti = type.mutiple; - // Add this force to target and source nodes - if (!source.isLocked) { - source.offsetX += forceX; - source.offsetY += forceY; - } + if (!prop || !prop.bypass) { + // need a bypass if one doesn't exist + this.applyBypass(ele, name, value); + continue; + } - if (!target.isLocked) { - target.offsetX -= forceX; - target.offsetY -= forceY; - } + var oldValue = prop.pfValue != null ? prop.pfValue : prop.value; - // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id; - // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")"; - // logDebug(s); - } - }; + prop.value = value; - /** - * @brief : Computes gravity forces for all nodes - */ - var calculateGravityForces = function calculateGravityForces(layoutInfo, options) { - var distThreshold = 1; + if (prop.pfValue != null) { + prop.pfValue = value; + } - // var s = 'calculateGravityForces'; - // logDebug(s); - for (var i = 0; i < layoutInfo.graphSet.length; i++) { - var graph = layoutInfo.graphSet[i]; - var numNodes = graph.length; + if (isColor) { + prop.strValue = 'rgb(' + value.join(',') + ')'; + } else if (isMulti) { + prop.strValue = value.join(' '); + } else { + prop.strValue = '' + value; + } - // s = "Set: " + graph.toString(); - // logDebug(s); + this.checkZOrderTrigger(ele, name, oldValue, value); + } +}; - // Compute graph center - if (0 === i) { - var centerX = layoutInfo.clientHeight / 2; - var centerY = layoutInfo.clientWidth / 2; - } else { - // Get Parent node for this graph, and use its position as center - var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]]; - var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]]; - var centerX = parent.positionX; - var centerY = parent.positionY; - } - // s = "Center found at: " + centerX + ", " + centerY; - // logDebug(s); +styfn.removeAllBypasses = function (eles, updateTransitions) { + return this.removeBypasses(eles, this.propertyNames, updateTransitions); +}; - // Apply force to all nodes in graph - for (var j = 0; j < numNodes; j++) { - var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; - // s = "Node: " + node.id; +styfn.removeBypasses = function (eles, props, updateTransitions) { + var isBypass = true; - if (node.isLocked) { - continue; - } + for (var j = 0; j < eles.length; j++) { + var ele = eles[j]; + var diffProps = {}; - var dx = centerX - node.positionX; - var dy = centerY - node.positionY; - var d = Math.sqrt(dx * dx + dy * dy); - if (d > distThreshold) { - var fx = options.gravity * dx / d; - var fy = options.gravity * dy / d; - node.offsetX += fx; - node.offsetY += fy; - // s += ": Applied force: " + fx + ", " + fy; - } else {} - // s += ": skypped since it's too close to center"; + for (var i = 0; i < props.length; i++) { + var name = props[i]; + var prop = this.properties[name]; + var prevProp = ele.pstyle(prop.name); - // logDebug(s); - } + if (!prevProp || !prevProp.bypass) { + // if a bypass doesn't exist for the prop, nothing needs to be removed + continue; } - }; - - /** - * @brief : This function propagates the existing offsets from - * parent nodes to its descendents. - * @arg layoutInfo : layoutInfo Object - * @arg cy : cytoscape Object - * @arg options : Layout options - */ - var propagateForces = function propagateForces(layoutInfo, options) { - // Inline implementation of a queue, used for traversing the graph in BFS order - var queue = []; - var start = 0; // Points to the start the queue - var end = -1; // Points to the end of the queue - - // logDebug('propagateForces'); - // Start by visiting the nodes in the root graph - queue.push.apply(queue, layoutInfo.graphSet[0]); - end += layoutInfo.graphSet[0].length; + var value = ''; // empty => remove bypass + var parsedProp = this.parse(name, value, true); + var diffProp = diffProps[prop.name] = { prev: prevProp }; - // Traverse the graph, level by level, - while (start <= end) { - // Get the node to visit and remove it from queue - var nodeId = queue[start++]; - var nodeIndex = layoutInfo.idToIndex[nodeId]; - var node = layoutInfo.layoutNodes[nodeIndex]; - var children = node.children; + this.applyParsedProperty(ele, parsedProp); - // We only need to process the node if it's compound - if (0 < children.length && !node.isLocked) { - var offX = node.offsetX; - var offY = node.offsetY; + diffProp.next = ele.pstyle(prop.name); + } // for props - // var s = "Propagating offset from parent node : " + node.id + - // ". OffsetX: " + offX + ". OffsetY: " + offY; - // s += "\n Children: " + children.toString(); - // logDebug(s); + this.updateStyleHints(ele); - for (var i = 0; i < children.length; i++) { - var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; - // Propagate offset - childNode.offsetX += offX; - childNode.offsetY += offY; - // Add children to queue to be visited - queue[++end] = children[i]; - } + if (updateTransitions) { + this.updateTransitions(ele, diffProps, isBypass); + } + } // for eles +}; - // Reset parent offsets - node.offsetX = 0; - node.offsetY = 0; - } - } - }; +module.exports = styfn; - /** - * @brief : Updates the layout model positions, based on - * the accumulated forces - */ - var updatePositions = function updatePositions(layoutInfo, options) { - // var s = 'Updating positions'; - // logDebug(s); +/***/ }), +/* 87 */ +/***/ (function(module, exports, __webpack_require__) { - // Reset boundaries for compound nodes - for (var i = 0; i < layoutInfo.nodeSize; i++) { - var n = layoutInfo.layoutNodes[i]; - if (0 < n.children.length) { - // logDebug("Resetting boundaries of compound node: " + n.id); - n.maxX = undefined; - n.minX = undefined; - n.maxY = undefined; - n.minY = undefined; - } - } +"use strict"; - for (var i = 0; i < layoutInfo.nodeSize; i++) { - var n = layoutInfo.layoutNodes[i]; - if (0 < n.children.length || n.isLocked) { - // No need to set compound or locked node position - // logDebug("Skipping position update of node: " + n.id); - continue; - } - // s = "Node: " + n.id + " Previous position: (" + - // n.positionX + ", " + n.positionY + ")."; - // Limit displacement in order to improve stability - var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature); - n.positionX += tempForce.x; - n.positionY += tempForce.y; - n.offsetX = 0; - n.offsetY = 0; - n.minX = n.positionX - n.width; - n.maxX = n.positionX + n.width; - n.minY = n.positionY - n.height; - n.maxY = n.positionY + n.height; - // s += " New Position: (" + n.positionX + ", " + n.positionY + ")."; - // logDebug(s); +var window = __webpack_require__(3); - // Update ancestry boudaries - updateAncestryBoundaries(n, layoutInfo); - } +var styfn = {}; - // Update size, position of compund nodes - for (var i = 0; i < layoutInfo.nodeSize; i++) { - var n = layoutInfo.layoutNodes[i]; - if (0 < n.children.length && !n.isLocked) { - n.positionX = (n.maxX + n.minX) / 2; - n.positionY = (n.maxY + n.minY) / 2; - n.width = n.maxX - n.minX; - n.height = n.maxY - n.minY; - // s = "Updating position, size of compound node " + n.id; - // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY; - // s += "\nWidth: " + n.width + ", Height: " + n.height; - // logDebug(s); - } - } - }; +// gets what an em size corresponds to in pixels relative to a dom element +styfn.getEmSizeInPixels = function () { + var px = this.containerCss('font-size'); - /** - * @brief : Limits a force (forceX, forceY) to be not - * greater (in modulo) than max. - 8 Preserves force direction. - */ - var limitForce = function limitForce(forceX, forceY, max) { - // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max; - var force = Math.sqrt(forceX * forceX + forceY * forceY); + if (px != null) { + return parseFloat(px); + } else { + return 1; // for headless + } +}; - if (force > max) { - var res = { - x: max * forceX / force, - y: max * forceY / force - }; - } else { - var res = { - x: forceX, - y: forceY - }; - } +// gets css property from the core container +styfn.containerCss = function (propName) { + var cy = this._private.cy; + var domElement = cy.container(); - // s += ".\nResult: (" + res.x + ", " + res.y + ")"; - // logDebug(s); + if (window && domElement && window.getComputedStyle) { + return window.getComputedStyle(domElement).getPropertyValue(propName); + } +}; - return res; - }; +module.exports = styfn; - /** - * @brief : Function used for keeping track of compound node - * sizes, since they should bound all their subnodes. - */ - var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) { - // var s = "Propagating new position/size of node " + node.id; - var parentId = node.parentId; - if (null == parentId) { - // If there's no parent, we are done - // s += ". No parent node."; - // logDebug(s); - return; - } +/***/ }), +/* 88 */ +/***/ (function(module, exports, __webpack_require__) { - // Get Parent Node - var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]]; - var flag = false; +"use strict"; - // MaxX - if (null == p.maxX || node.maxX + p.padRight > p.maxX) { - p.maxX = node.maxX + p.padRight; - flag = true; - // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX; - } - // MinX - if (null == p.minX || node.minX - p.padLeft < p.minX) { - p.minX = node.minX - p.padLeft; - flag = true; - // s += "\nNew minX for parent node " + p.id + ": " + p.minX; - } +var util = __webpack_require__(1); +var is = __webpack_require__(0); - // MaxY - if (null == p.maxY || node.maxY + p.padBottom > p.maxY) { - p.maxY = node.maxY + p.padBottom; - flag = true; - // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY; - } +var styfn = {}; - // MinY - if (null == p.minY || node.minY - p.padTop < p.minY) { - p.minY = node.minY - p.padTop; - flag = true; - // s += "\nNew minY for parent node " + p.id + ": " + p.minY; - } +// gets the rendered style for an element +styfn.getRenderedStyle = function (ele, prop) { + if (prop) { + return this.getStylePropertyValue(ele, prop, true); + } else { + return this.getRawStyle(ele, true); + } +}; - // If updated boundaries, propagate changes upward - if (flag) { - // logDebug(s); - return updateAncestryBoundaries(p, layoutInfo); - } +// gets the raw style for an element +styfn.getRawStyle = function (ele, isRenderedVal) { + var self = this; - // s += ". No changes in boundaries/position of parent node " + p.id; - // logDebug(s); - return; - }; + ele = ele[0]; // insure it's an element - var separateComponents = function separateComponents(layutInfo, options) { - var nodes = layoutInfo.layoutNodes; - var components = []; + if (ele) { + var rstyle = {}; - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var cid = node.cmptId; - var component = components[cid] = components[cid] || []; + for (var i = 0; i < self.properties.length; i++) { + var prop = self.properties[i]; + var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal); - component.push(node); + if (val != null) { + rstyle[prop.name] = val; + rstyle[util.dash2camel(prop.name)] = val; } + } - var totalA = 0; - - for (var i = 0; i < components.length; i++) { - var c = components[i]; + return rstyle; + } +}; - if (!c) { - continue; - } +styfn.getIndexedStyle = function (ele, property, subproperty, index) { + var pstyle = ele.pstyle(property)[subproperty][index]; + return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0]; +}; - c.x1 = Infinity; - c.x2 = -Infinity; - c.y1 = Infinity; - c.y2 = -Infinity; +styfn.getStylePropertyValue = function (ele, propName, isRenderedVal) { + var self = this; - for (var j = 0; j < c.length; j++) { - var n = c[j]; + ele = ele[0]; // insure it's an element - c.x1 = Math.min(c.x1, n.positionX - n.width / 2); - c.x2 = Math.max(c.x2, n.positionX + n.width / 2); - c.y1 = Math.min(c.y1, n.positionY - n.height / 2); - c.y2 = Math.max(c.y2, n.positionY + n.height / 2); - } + if (ele) { + var prop = self.properties[propName]; - c.w = c.x2 - c.x1; - c.h = c.y2 - c.y1; + if (prop.alias) { + prop = prop.pointsTo; + } - totalA += c.w * c.h; - } + var type = prop.type; + var styleProp = ele.pstyle(prop.name); + var zoom = ele.cy().zoom(); - components.sort(function (c1, c2) { - return c2.w * c2.h - c1.w * c1.h; - }); + if (styleProp) { + var units = styleProp.units ? type.implicitUnits || 'px' : null; + var val = units ? [].concat(styleProp.pfValue).map(function (pfValue) { + return pfValue * (isRenderedVal ? zoom : 1) + units; + }).join(' ') : styleProp.strValue; - var x = 0; - var y = 0; - var usedW = 0; - var rowH = 0; - var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight; + return val; + } + } +}; - for (var i = 0; i < components.length; i++) { - var c = components[i]; +styfn.getAnimationStartStyle = function (ele, aniProps) { + var rstyle = {}; - if (!c) { - continue; - } + for (var i = 0; i < aniProps.length; i++) { + var aniProp = aniProps[i]; + var name = aniProp.name; - for (var j = 0; j < c.length; j++) { - var n = c[j]; + var styleProp = ele.pstyle(name); - if (!n.isLocked) { - n.positionX += x; - n.positionY += y; - } - } + if (styleProp !== undefined) { + // then make a prop of it + if (is.plainObject(styleProp)) { + styleProp = this.parse(name, styleProp.strValue); + } else { + styleProp = this.parse(name, styleProp); + } + } - x += c.w + options.componentSpacing; - usedW += c.w + options.componentSpacing; - rowH = Math.max(rowH, c.h); + if (styleProp) { + rstyle[name] = styleProp; + } + } - if (usedW > maxRowW) { - y += rowH + options.componentSpacing; - x = 0; - usedW = 0; - rowH = 0; - } - } - }; + return rstyle; +}; - var mainLoop = function mainLoop(i) { - if (stopped) { - // logDebug("Layout manually stopped. Stopping computation in step " + i); - return false; - } +styfn.getPropsList = function (propsObj) { + var self = this; + var rstyle = []; + var style = propsObj; + var props = self.properties; - // Do one step in the phisical simulation - step(layoutInfo, options, i); + if (style) { + var names = Object.keys(style); - // Update temperature - layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; - // logDebug("New temperature: " + layoutInfo.temperature); + for (var i = 0; i < names.length; i++) { + var name = names[i]; + var val = style[name]; + var prop = props[name] || props[util.camel2dash(name)]; + var styleProp = this.parse(prop.name, val); - if (layoutInfo.temperature < options.minTemp) { - // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i); - return false; + if (styleProp) { + rstyle.push(styleProp); } + } + } - return true; - }; + return rstyle; +}; - var i = 0; - var loopRet; +module.exports = styfn; - do { - var f = 0; +/***/ }), +/* 89 */ +/***/ (function(module, exports, __webpack_require__) { - while (f < options.refresh && i < options.numIter) { - var loopRet = mainLoop(i); - if (!loopRet) { - break; - } +"use strict"; - f++; - i++; - } - if (options.animate === true) { - broadcast(layoutInfo.layoutNodes); // eslint-disable-line no-undef - } - } while (loopRet && i + 1 < options.numIter); +var styfn = {}; - separateComponents(layoutInfo, options); +styfn.appendFromJson = function (json) { + var style = this; - return layoutInfo; - }).then(function (layoutInfoUpdated) { - layoutInfo.layoutNodes = layoutInfoUpdated.layoutNodes; // get the positions + for (var i = 0; i < json.length; i++) { + var context = json[i]; + var selector = context.selector; + var props = context.style || context.css; + var names = Object.keys(props); - thread.stop(); - done(); - }); + style.selector(selector); // apply selector - var done = function done() { - if (options.animate === true || options.animate === false) { - refresh({ - force: true, - next: function next() { - // Layout has finished - layout.one('layoutstop', options.stop); - layout.emit({ type: 'layoutstop', layout: layout }); - } - }); - } else { - options.eles.nodes().layoutPositions(layout, options, function (node) { - var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]]; + for (var j = 0; j < names.length; j++) { + var name = names[j]; + var value = props[name]; - return { x: lnode.positionX, y: lnode.positionY }; - }); + style.css(name, value); // apply property } - }; + } - return this; // chaining + return style; }; -/** - * @brief : called on continuous layouts to stop them before they finish - */ -CoseLayout.prototype.stop = function () { - this.stopped = true; - - if (this.thread) { - this.thread.stop(); - } +// accessible cy.style() function +styfn.fromJson = function (json) { + var style = this; - this.emit('layoutstop'); + style.resetToDefault(); + style.appendFromJson(json); - return this; // chaining + return style; }; -CoseLayout.prototype.destroy = function () { - if (this.thread) { - this.thread.stop(); - } +// get json from cy.style() api +styfn.json = function () { + var json = []; - return this; // chaining -}; + for (var i = this.defaultLength; i < this.length; i++) { + var cxt = this[i]; + var selector = cxt.selector; + var props = cxt.properties; + var css = {}; -/** - * @brief : Creates an object which is contains all the data - * used in the layout process - * @arg cy : cytoscape.js object - * @return : layoutInfo object initialized - */ -var createLayoutInfo = function createLayoutInfo(cy, layout, options) { - // Shortcut - var edges = options.eles.edges(); - var nodes = options.eles.nodes(); + for (var j = 0; j < props.length; j++) { + var prop = props[j]; + css[prop.name] = prop.strValue; + } - var layoutInfo = { - isCompound: cy.hasCompoundNodes(), - layoutNodes: [], - idToIndex: {}, - nodeSize: nodes.size(), - graphSet: [], - indexToGraph: [], - layoutEdges: [], - edgeSize: edges.size(), - temperature: options.initialTemp, - clientWidth: cy.width(), - clientHeight: cy.width(), - boundingBox: math.makeBoundingBox(options.boundingBox ? options.boundingBox : { - x1: 0, y1: 0, w: cy.width(), h: cy.height() - }) - }; + json.push({ + selector: !selector ? 'core' : selector.toString(), + style: css + }); + } - var components = options.eles.components(); - var id2cmptId = {}; + return json; +}; - for (var i = 0; i < components.length; i++) { - var component = components[i]; +module.exports = styfn; - for (var j = 0; j < component.length; j++) { - var node = component[j]; +/***/ }), +/* 90 */ +/***/ (function(module, exports, __webpack_require__) { - id2cmptId[node.id()] = i; - } - } +"use strict"; - // Iterate over all nodes, creating layout nodes - for (var i = 0; i < layoutInfo.nodeSize; i++) { - var n = nodes[i]; - var nbb = n.layoutDimensions(options); - var tempNode = {}; - tempNode.isLocked = n.locked(); - tempNode.id = n.data('id'); - tempNode.parentId = n.data('parent'); - tempNode.cmptId = id2cmptId[n.id()]; - tempNode.children = []; - tempNode.positionX = n.position('x'); - tempNode.positionY = n.position('y'); - tempNode.offsetX = 0; - tempNode.offsetY = 0; - tempNode.height = nbb.w; - tempNode.width = nbb.h; - tempNode.maxX = tempNode.positionX + tempNode.width / 2; - tempNode.minX = tempNode.positionX - tempNode.width / 2; - tempNode.maxY = tempNode.positionY + tempNode.height / 2; - tempNode.minY = tempNode.positionY - tempNode.height / 2; - tempNode.padLeft = parseFloat(n.style('padding')); - tempNode.padRight = parseFloat(n.style('padding')); - tempNode.padTop = parseFloat(n.style('padding')); - tempNode.padBottom = parseFloat(n.style('padding')); +var util = __webpack_require__(1); +var Selector = __webpack_require__(6); - // forces - tempNode.nodeRepulsion = is.fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; +var styfn = {}; - // Add new node - layoutInfo.layoutNodes.push(tempNode); - // Add entry to id-index map - layoutInfo.idToIndex[tempNode.id] = i; - } +styfn.appendFromString = function (string) { + var self = this; + var style = this; + var remaining = '' + string; + var selAndBlockStr = void 0; + var blockRem = void 0; + var propAndValStr = void 0; - // Inline implementation of a queue, used for traversing the graph in BFS order - var queue = []; - var start = 0; // Points to the start the queue - var end = -1; // Points to the end of the queue + // remove comments from the style string + remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, ''); - var tempGraph = []; + function removeSelAndBlockFromRemaining() { + // remove the parsed selector and block from the remaining text to parse + if (remaining.length > selAndBlockStr.length) { + remaining = remaining.substr(selAndBlockStr.length); + } else { + remaining = ''; + } + } - // Second pass to add child information and - // initialize queue for hierarchical traversal - for (var i = 0; i < layoutInfo.nodeSize; i++) { - var n = layoutInfo.layoutNodes[i]; - var p_id = n.parentId; - // Check if node n has a parent node - if (null != p_id) { - // Add node Id to parent's list of children - layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id); + function removePropAndValFromRem() { + // remove the parsed property and value from the remaining block text to parse + if (blockRem.length > propAndValStr.length) { + blockRem = blockRem.substr(propAndValStr.length); } else { - // If a node doesn't have a parent, then it's in the root graph - queue[++end] = n.id; - tempGraph.push(n.id); + blockRem = ''; } } - // Add root graph to graphSet - layoutInfo.graphSet.push(tempGraph); + while (true) { + var nothingLeftToParse = remaining.match(/^\s*$/); + if (nothingLeftToParse) { + break; + } - // Traverse the graph, level by level, - while (start <= end) { - // Get the node to visit and remove it from queue - var node_id = queue[start++]; - var node_ix = layoutInfo.idToIndex[node_id]; - var node = layoutInfo.layoutNodes[node_ix]; - var children = node.children; - if (children.length > 0) { - // Add children nodes as a new graph to graph set - layoutInfo.graphSet.push(children); - // Add children to que queue to be visited - for (var i = 0; i < children.length; i++) { - queue[++end] = children[i]; - } + var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/); + + if (!selAndBlock) { + util.error('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining); + break; } - } - // Create indexToGraph map - for (var i = 0; i < layoutInfo.graphSet.length; i++) { - var graph = layoutInfo.graphSet[i]; - for (var j = 0; j < graph.length; j++) { - var index = layoutInfo.idToIndex[graph[j]]; - layoutInfo.indexToGraph[index] = i; + selAndBlockStr = selAndBlock[0]; + + // parse the selector + var selectorStr = selAndBlock[1]; + if (selectorStr !== 'core') { + var selector = new Selector(selectorStr); + if (selector._private.invalid) { + util.error('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); + + // skip this selector and block + removeSelAndBlockFromRemaining(); + continue; + } } - } - // Iterate over all edges, creating Layout Edges - for (var i = 0; i < layoutInfo.edgeSize; i++) { - var e = edges[i]; - var tempEdge = {}; - tempEdge.id = e.data('id'); - tempEdge.sourceId = e.data('source'); - tempEdge.targetId = e.data('target'); + // parse the block of properties and values + var blockStr = selAndBlock[2]; + var invalidBlock = false; + blockRem = blockStr; + var props = []; - // Compute ideal length - var idealLength = is.fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength; - var elasticity = is.fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; + while (true) { + var _nothingLeftToParse = blockRem.match(/^\s*$/); + if (_nothingLeftToParse) { + break; + } - // Check if it's an inter graph edge - var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId]; - var targetIx = layoutInfo.idToIndex[tempEdge.targetId]; - var sourceGraph = layoutInfo.indexToGraph[sourceIx]; - var targetGraph = layoutInfo.indexToGraph[targetIx]; + var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/); - if (sourceGraph != targetGraph) { - // Find lowest common graph ancestor - var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); + if (!propAndVal) { + util.error('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr); + invalidBlock = true; + break; + } - // Compute sum of node depths, relative to lca graph - var lcaGraph = layoutInfo.graphSet[lca]; - var depth = 0; + propAndValStr = propAndVal[0]; + var propStr = propAndVal[1]; + var valStr = propAndVal[2]; - // Source depth - var tempNode = layoutInfo.layoutNodes[sourceIx]; - while (-1 === lcaGraph.indexOf(tempNode.id)) { - tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]]; - depth++; + var prop = self.properties[propStr]; + if (!prop) { + util.error('Skipping property: Invalid property name in: ' + propAndValStr); + + // skip this property in the block + removePropAndValFromRem(); + continue; } - // Target depth - tempNode = layoutInfo.layoutNodes[targetIx]; - while (-1 === lcaGraph.indexOf(tempNode.id)) { - tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]]; - depth++; + var parsedProp = style.parse(propStr, valStr); + + if (!parsedProp) { + util.error('Skipping property: Invalid property definition in: ' + propAndValStr); + + // skip this property in the block + removePropAndValFromRem(); + continue; } - // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId + - // ". Index: " + lca + " Contents: " + lcaGraph.toString() + - // ". Depth: " + depth); + props.push({ + name: propStr, + val: valStr + }); + removePropAndValFromRem(); + } - // Update idealLength - idealLength *= depth * options.nestingFactor; + if (invalidBlock) { + removeSelAndBlockFromRemaining(); + break; } - tempEdge.idealLength = idealLength; - tempEdge.elasticity = elasticity; + // put the parsed block in the style + style.selector(selectorStr); + for (var i = 0; i < props.length; i++) { + var _prop = props[i]; + style.css(_prop.name, _prop.val); + } - layoutInfo.layoutEdges.push(tempEdge); + removeSelAndBlockFromRemaining(); } - // Finally, return layoutInfo object - return layoutInfo; + return style; }; -/** - * @brief : This function finds the index of the lowest common - * graph ancestor between 2 nodes in the subtree - * (from the graph hierarchy induced tree) whose - * root is graphIx - * - * @arg node1: node1's ID - * @arg node2: node2's ID - * @arg layoutInfo: layoutInfo object - * - */ -var findLCA = function findLCA(node1, node2, layoutInfo) { - // Find their common ancester, starting from the root graph - var res = findLCA_aux(node1, node2, 0, layoutInfo); - if (2 > res.count) { - // If aux function couldn't find the common ancester, - // then it is the root graph - return 0; - } else { - return res.graph; - } +styfn.fromString = function (string) { + var style = this; + + style.resetToDefault(); + style.appendFromString(string); + + return style; }; -/** - * @brief : Auxiliary function used for LCA computation - * - * @arg node1 : node1's ID - * @arg node2 : node2's ID - * @arg graphIx : subgraph index - * @arg layoutInfo : layoutInfo object - * - * @return : object of the form {count: X, graph: Y}, where: - * X is the number of ancesters (max: 2) found in - * graphIx (and it's subgraphs), - * Y is the graph index of the lowest graph containing - * all X nodes - */ -var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) { - var graph = layoutInfo.graphSet[graphIx]; - // If both nodes belongs to graphIx - if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) { - return { count: 2, graph: graphIx }; - } - - // Make recursive calls for all subgraphs - var c = 0; - for (var i = 0; i < graph.length; i++) { - var nodeId = graph[i]; - var nodeIx = layoutInfo.idToIndex[nodeId]; - var children = layoutInfo.layoutNodes[nodeIx].children; - - // If the node has no child, skip it - if (0 === children.length) { - continue; - } - - var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]]; - var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo); - if (0 === result.count) { - // Neither node1 nor node2 are present in this subgraph - continue; - } else if (1 === result.count) { - // One of (node1, node2) is present in this subgraph - c++; - if (2 === c) { - // We've already found both nodes, no need to keep searching - break; - } - } else { - // Both nodes are present in this subgraph - return result; - } - } - - return { count: c, graph: graphIx }; -}; - -/** - * @brief: printsLayoutInfo into js console - * Only used for debbuging - */ -var printLayoutInfo = function printLayoutInfo(layoutInfo) { - /* eslint-disable */ - - if (!DEBUG) { - return; - } - console.debug('layoutNodes:'); - for (var i = 0; i < layoutInfo.nodeSize; i++) { - var n = layoutInfo.layoutNodes[i]; - var s = '\nindex: ' + i + '\nId: ' + n.id + '\nChildren: ' + n.children.toString() + '\nparentId: ' + n.parentId + '\npositionX: ' + n.positionX + '\npositionY: ' + n.positionY + '\nOffsetX: ' + n.offsetX + '\nOffsetY: ' + n.offsetY + '\npadLeft: ' + n.padLeft + '\npadRight: ' + n.padRight + '\npadTop: ' + n.padTop + '\npadBottom: ' + n.padBottom; - - console.debug(s); - } - - console.debug('idToIndex'); - for (var i in layoutInfo.idToIndex) { - console.debug('Id: ' + i + '\nIndex: ' + layoutInfo.idToIndex[i]); - } - - console.debug('Graph Set'); - var set = layoutInfo.graphSet; - for (var i = 0; i < set.length; i++) { - console.debug('Set : ' + i + ': ' + set[i].toString()); - } - - var s = 'IndexToGraph'; - for (var i = 0; i < layoutInfo.indexToGraph.length; i++) { - s += '\nIndex : ' + i + ' Graph: ' + layoutInfo.indexToGraph[i]; - } - console.debug(s); - - s = 'Layout Edges'; - for (var i = 0; i < layoutInfo.layoutEdges.length; i++) { - var e = layoutInfo.layoutEdges[i]; - s += '\nEdge Index: ' + i + ' ID: ' + e.id + ' SouceID: ' + e.sourceId + ' TargetId: ' + e.targetId + ' Ideal Length: ' + e.idealLength; - } - console.debug(s); - - s = 'nodeSize: ' + layoutInfo.nodeSize; - s += '\nedgeSize: ' + layoutInfo.edgeSize; - s += '\ntemperature: ' + layoutInfo.temperature; - console.debug(s); - - return; - /* eslint-enable */ -}; - -/** - * @brief : Randomizes the position of all nodes - */ -var randomizePositions = function randomizePositions(layoutInfo, cy) { - var width = layoutInfo.clientWidth; - var height = layoutInfo.clientHeight; - - for (var i = 0; i < layoutInfo.nodeSize; i++) { - var n = layoutInfo.layoutNodes[i]; - - // No need to randomize compound nodes or locked nodes - if (0 === n.children.length && !n.isLocked) { - n.positionX = Math.random() * width; - n.positionY = Math.random() * height; - } - } -}; - -/** - * @brief : Updates the positions of nodes in the network - * @arg layoutInfo : LayoutInfo object - * @arg cy : Cytoscape object - * @arg options : Layout options - */ -var refreshPositions = function refreshPositions(layoutInfo, cy, options) { - // var s = 'Refreshing positions'; - // logDebug(s); - - var layout = options.layout; - var nodes = options.eles.nodes(); - var bb = layoutInfo.boundingBox; - var coseBB = { x1: Infinity, x2: -Infinity, y1: Infinity, y2: -Infinity }; - - if (options.boundingBox) { - nodes.forEach(function (node) { - var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]]; - - coseBB.x1 = Math.min(coseBB.x1, lnode.positionX); - coseBB.x2 = Math.max(coseBB.x2, lnode.positionX); - - coseBB.y1 = Math.min(coseBB.y1, lnode.positionY); - coseBB.y2 = Math.max(coseBB.y2, lnode.positionY); - }); - - coseBB.w = coseBB.x2 - coseBB.x1; - coseBB.h = coseBB.y2 - coseBB.y1; - } - - nodes.positions(function (ele, i) { - var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]]; - // s = "Node: " + lnode.id + ". Refreshed position: (" + - // lnode.positionX + ", " + lnode.positionY + ")."; - // logDebug(s); - - if (options.boundingBox) { - // then add extra bounding box constraint - var pctX = (lnode.positionX - coseBB.x1) / coseBB.w; - var pctY = (lnode.positionY - coseBB.y1) / coseBB.h; - - return { - x: bb.x1 + pctX * bb.w, - y: bb.y1 + pctY * bb.h - }; - } else { - return { - x: lnode.positionX, - y: lnode.positionY - }; - } - }); - - // Trigger layoutReady only on first call - if (true !== layoutInfo.ready) { - // s = 'Triggering layoutready'; - // logDebug(s); - layoutInfo.ready = true; - layout.one('layoutready', options.ready); - layout.emit({ type: 'layoutready', layout: this }); - } -}; - -/** - * @brief : Logs a debug message in JS console, if DEBUG is ON - */ -// var logDebug = function(text) { -// if (DEBUG) { -// console.debug(text); -// } -// }; - -module.exports = CoseLayout; - -/***/ }), -/* 80 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(1); -var math = __webpack_require__(2); - -var defaults = { - fit: true, // whether to fit the viewport to the graph - padding: 30, // padding used on fit - boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } - avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space - avoidOverlapPadding: 10, // extra spacing around nodes when avoidOverlap: true - nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm - spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up - condense: false, // uses all available space on false, uses minimal space on true - rows: undefined, // force num of rows in the grid - cols: undefined, // force num of columns in the grid - position: function position(node) {}, // returns { row, col } for element - sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') } - animate: false, // whether to transition the node positions - animationDuration: 500, // duration of animation in ms if enabled - animationEasing: undefined, // easing of animation if enabled - animateFilter: function animateFilter(node, i) { - return true; - }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts - ready: undefined, // callback on layoutready - stop: undefined, // callback on layoutstop - transform: function transform(node, position) { - return position; - } // transform a given node position. Useful for changing flow direction in discrete layouts -}; - -function GridLayout(options) { - this.options = util.extend({}, defaults, options); -} - -GridLayout.prototype.run = function () { - var params = this.options; - var options = params; - - var cy = params.cy; - var eles = options.eles; - var nodes = eles.nodes().not(':parent'); - - if (options.sort) { - nodes = nodes.sort(options.sort); - } - - var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { - x1: 0, y1: 0, w: cy.width(), h: cy.height() - }); - - if (bb.h === 0 || bb.w === 0) { - nodes.layoutPositions(this, options, function (ele) { - return { x: bb.x1, y: bb.y1 }; - }); - } else { - - // width/height * splits^2 = cells where splits is number of times to split width - var cells = nodes.size(); - var splits = Math.sqrt(cells * bb.h / bb.w); - var rows = Math.round(splits); - var cols = Math.round(bb.w / bb.h * splits); - - var small = function small(val) { - if (val == null) { - return Math.min(rows, cols); - } else { - var min = Math.min(rows, cols); - if (min == rows) { - rows = val; - } else { - cols = val; - } - } - }; - - var large = function large(val) { - if (val == null) { - return Math.max(rows, cols); - } else { - var max = Math.max(rows, cols); - if (max == rows) { - rows = val; - } else { - cols = val; - } - } - }; - - var oRows = options.rows; - var oCols = options.cols != null ? options.cols : options.columns; - - // if rows or columns were set in options, use those values - if (oRows != null && oCols != null) { - rows = oRows; - cols = oCols; - } else if (oRows != null && oCols == null) { - rows = oRows; - cols = Math.ceil(cells / rows); - } else if (oRows == null && oCols != null) { - cols = oCols; - rows = Math.ceil(cells / cols); - } - - // otherwise use the automatic values and adjust accordingly - - // if rounding was up, see if we can reduce rows or columns - else if (cols * rows > cells) { - var sm = small(); - var lg = large(); - - // reducing the small side takes away the most cells, so try it first - if ((sm - 1) * lg >= cells) { - small(sm - 1); - } else if ((lg - 1) * sm >= cells) { - large(lg - 1); - } - } else { - - // if rounding was too low, add rows or columns - while (cols * rows < cells) { - var _sm = small(); - var _lg = large(); - - // try to add to larger side first (adds less in multiplication) - if ((_lg + 1) * _sm >= cells) { - large(_lg + 1); - } else { - small(_sm + 1); - } - } - } - - var cellWidth = bb.w / cols; - var cellHeight = bb.h / rows; - - if (options.condense) { - cellWidth = 0; - cellHeight = 0; - } - - if (options.avoidOverlap) { - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - var pos = node._private.position; - - if (pos.x == null || pos.y == null) { - // for bb - pos.x = 0; - pos.y = 0; - } - - var nbb = node.layoutDimensions(options); - var p = options.avoidOverlapPadding; - - var w = nbb.w + p; - var h = nbb.h + p; - - cellWidth = Math.max(cellWidth, w); - cellHeight = Math.max(cellHeight, h); - } - } - - var cellUsed = {}; // e.g. 'c-0-2' => true - - var used = function used(row, col) { - return cellUsed['c-' + row + '-' + col] ? true : false; - }; - - var use = function use(row, col) { - cellUsed['c-' + row + '-' + col] = true; - }; - - // to keep track of current cell position - var row = 0; - var col = 0; - var moveToNextCell = function moveToNextCell() { - col++; - if (col >= cols) { - col = 0; - row++; - } - }; - - // get a cache of all the manual positions - var id2manPos = {}; - for (var _i = 0; _i < nodes.length; _i++) { - var _node = nodes[_i]; - var rcPos = options.position(_node); - - if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) { - // must have at least row or col def'd - var _pos = { - row: rcPos.row, - col: rcPos.col - }; - - if (_pos.col === undefined) { - // find unused col - _pos.col = 0; - - while (used(_pos.row, _pos.col)) { - _pos.col++; - } - } else if (_pos.row === undefined) { - // find unused row - _pos.row = 0; - - while (used(_pos.row, _pos.col)) { - _pos.row++; - } - } - - id2manPos[_node.id()] = _pos; - use(_pos.row, _pos.col); - } - } - - var getPos = function getPos(element, i) { - var x = void 0, - y = void 0; - - if (element.locked() || element.isParent()) { - return false; - } - - // see if we have a manual position set - var rcPos = id2manPos[element.id()]; - if (rcPos) { - x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1; - y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1; - } else { - // otherwise set automatically - - while (used(row, col)) { - moveToNextCell(); - } - - x = col * cellWidth + cellWidth / 2 + bb.x1; - y = row * cellHeight + cellHeight / 2 + bb.y1; - use(row, col); - - moveToNextCell(); - } - - return { x: x, y: y }; - }; - - nodes.layoutPositions(this, options, getPos); - } - - return this; // chaining -}; - -module.exports = GridLayout; - -/***/ }), -/* 81 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = [{ name: 'breadthfirst', impl: __webpack_require__(76) }, { name: 'circle', impl: __webpack_require__(77) }, { name: 'concentric', impl: __webpack_require__(78) }, { name: 'cose', impl: __webpack_require__(79) }, { name: 'grid', impl: __webpack_require__(80) }, { name: 'null', impl: __webpack_require__(82) }, { name: 'preset', impl: __webpack_require__(83) }, { name: 'random', impl: __webpack_require__(84) }]; - -/***/ }), -/* 82 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(1); - -// default layout options -var defaults = { - ready: function ready() {}, // on layoutready - stop: function stop() {} // on layoutstop -}; - -// constructor -// options : object containing layout options -function NullLayout(options) { - this.options = util.extend({}, defaults, options); -} - -// runs the layout -NullLayout.prototype.run = function () { - var options = this.options; - var eles = options.eles; // elements to consider in the layout - var layout = this; - - // cy is automatically populated for us in the constructor - var cy = options.cy; // jshint ignore:line - - layout.emit('layoutstart'); - - // puts all nodes at (0, 0) - eles.nodes().positions(function () { - return { - x: 0, - y: 0 - }; - }); - - // trigger layoutready when each node has had its position set at least once - layout.one('layoutready', options.ready); - layout.emit('layoutready'); - - // trigger layoutstop when the layout stops (e.g. finishes) - layout.one('layoutstop', options.stop); - layout.emit('layoutstop'); - - return this; // chaining -}; - -// called on continuous layouts to stop them before they finish -NullLayout.prototype.stop = function () { - return this; // chaining -}; - -module.exports = NullLayout; - -/***/ }), -/* 83 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(1); -var is = __webpack_require__(0); - -var defaults = { - positions: undefined, // map of (node id) => (position obj); or function(node){ return somPos; } - zoom: undefined, // the zoom level to set (prob want fit = false if set) - pan: undefined, // the pan level to set (prob want fit = false if set) - fit: true, // whether to fit to viewport - padding: 30, // padding on fit - animate: false, // whether to transition the node positions - animationDuration: 500, // duration of animation in ms if enabled - animationEasing: undefined, // easing of animation if enabled - animateFilter: function animateFilter(node, i) { - return true; - }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts - ready: undefined, // callback on layoutready - stop: undefined, // callback on layoutstop - transform: function transform(node, position) { - return position; - } // transform a given node position. Useful for changing flow direction in discrete layouts -}; - -function PresetLayout(options) { - this.options = util.extend({}, defaults, options); -} - -PresetLayout.prototype.run = function () { - var options = this.options; - var eles = options.eles; - - var nodes = eles.nodes(); - var posIsFn = is.fn(options.positions); - - function getPosition(node) { - if (options.positions == null) { - return null; - } - - if (posIsFn) { - return options.positions(node); - } - - var pos = options.positions[node._private.data.id]; - - if (pos == null) { - return null; - } - - return pos; - } - - nodes.layoutPositions(this, options, function (node, i) { - var position = getPosition(node); - - if (node.locked() || position == null) { - return false; - } - - return position; - }); - - return this; // chaining -}; - -module.exports = PresetLayout; - -/***/ }), -/* 84 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(1); -var math = __webpack_require__(2); - -var defaults = { - fit: true, // whether to fit to viewport - padding: 30, // fit padding - boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } - animate: false, // whether to transition the node positions - animationDuration: 500, // duration of animation in ms if enabled - animationEasing: undefined, // easing of animation if enabled - animateFilter: function animateFilter(node, i) { - return true; - }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts - ready: undefined, // callback on layoutready - stop: undefined, // callback on layoutstop - transform: function transform(node, position) { - return position; - } // transform a given node position. Useful for changing flow direction in discrete layouts -}; - -function RandomLayout(options) { - this.options = util.extend({}, defaults, options); -} - -RandomLayout.prototype.run = function () { - var options = this.options; - var cy = options.cy; - var eles = options.eles; - var nodes = eles.nodes().not(':parent'); - - var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { - x1: 0, y1: 0, w: cy.width(), h: cy.height() - }); - - var getPos = function getPos(node, i) { - return { - x: bb.x1 + Math.round(Math.random() * bb.w), - y: bb.y1 + Math.round(Math.random() * bb.h) - }; - }; - - nodes.layoutPositions(this, options, getPos); - - return this; // chaining -}; - -module.exports = RandomLayout; +module.exports = styfn; /***/ }), -/* 85 */ +/* 91 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var math = __webpack_require__(2); -var is = __webpack_require__(0); var util = __webpack_require__(1); +var is = __webpack_require__(0); -var BRp = {}; - -BRp.arrowShapeWidth = 0.3; - -BRp.registerArrowShapes = function () { - var arrowShapes = this.arrowShapes = {}; - var renderer = this; - - // Contract for arrow shapes: - // 0, 0 is arrow tip - // (0, 1) is direction towards node - // (1, 0) is right - // - // functional api: - // collide: check x, y in shape - // roughCollide: called before collide, no false negatives - // draw: draw - // spacing: dist(arrowTip, nodeBoundary) - // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip - - var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) { - var x1 = translation.x - size / 2 - padding; - var x2 = translation.x + size / 2 + padding; - var y1 = translation.y - size / 2 - padding; - var y2 = translation.y + size / 2 + padding; - - var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2; +var styfn = {}; - return inside; +(function () { + var number = util.regex.number; + var rgba = util.regex.rgbaNoBackRefs; + var hsla = util.regex.hslaNoBackRefs; + var hex3 = util.regex.hex3; + var hex6 = util.regex.hex6; + var data = function data(prefix) { + return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$'; }; + var mapData = function mapData(prefix) { + var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3 + '|' + hex6; + return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$'; + }; + var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; - var transform = function transform(x, y, size, angle, translation) { - var xRotated = x * Math.cos(angle) - y * Math.sin(angle); - var yRotated = x * Math.sin(angle) + y * Math.cos(angle); - - var xScaled = xRotated * size; - var yScaled = yRotated * size; - - var xTranslated = xScaled + translation.x; - var yTranslated = yScaled + translation.y; + // each visual style property has a type and needs to be validated according to it + styfn.types = { + time: { number: true, min: 0, units: 's|ms', implicitUnits: 'ms' }, + percent: { number: true, min: 0, max: 100, units: '%', implicitUnits: '%' }, + zeroOneNumber: { number: true, min: 0, max: 1, unitless: true }, + zeroOneNumbers: { number: true, min: 0, max: 1, unitless: true, multiple: true }, + nOneOneNumber: { number: true, min: -1, max: 1, unitless: true }, + nonNegativeInt: { number: true, min: 0, integer: true, unitless: true }, + position: { enums: ['parent', 'origin'] }, + nodeSize: { number: true, min: 0, enums: ['label'] }, + number: { number: true, unitless: true }, + numbers: { number: true, unitless: true, multiple: true }, + positiveNumber: { number: true, unitless: true, min: 0, strictMin: true }, + size: { number: true, min: 0 }, + bidirectionalSize: { number: true }, // allows negative + bidirectionalSizes: { number: true, multiple: true }, // allows negative + sizeMaybePercent: { number: true, min: 0, allowPercent: true }, + paddingRelativeTo: { enums: ['width', 'height', 'average', 'min', 'max'] }, + bgWH: { number: true, min: 0, allowPercent: true, enums: ['auto'], multiple: true }, + bgPos: { number: true, allowPercent: true, multiple: true }, + bgRelativeTo: { enums: ['inner', 'include-padding'], multiple: true }, + bgRepeat: { enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'], multiple: true }, + bgFit: { enums: ['none', 'contain', 'cover'], multiple: true }, + bgCrossOrigin: { enums: ['anonymous', 'use-credentials'], multiple: true }, + bgClip: { enums: ['none', 'node'] }, + color: { color: true }, + bool: { enums: ['yes', 'no'] }, + lineStyle: { enums: ['solid', 'dotted', 'dashed'] }, + borderStyle: { enums: ['solid', 'dotted', 'dashed', 'double'] }, + curveStyle: { enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments'] }, + fontFamily: { regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$' }, + fontletiant: { enums: ['small-caps', 'normal'] }, + fontStyle: { enums: ['italic', 'normal', 'oblique'] }, + fontWeight: { enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900] }, + textDecoration: { enums: ['none', 'underline', 'overline', 'line-through'] }, + textTransform: { enums: ['none', 'uppercase', 'lowercase'] }, + textWrap: { enums: ['none', 'wrap', 'ellipsis'] }, + textBackgroundShape: { enums: ['rectangle', 'roundrectangle'] }, + nodeShape: { enums: ['rectangle', 'roundrectangle', 'cutrectangle', 'bottomroundrectangle', 'barrel', 'ellipse', 'triangle', 'square', 'pentagon', 'hexagon', 'concavehexagon', 'heptagon', 'octagon', 'tag', 'star', 'diamond', 'vee', 'rhomboid', 'polygon'] }, + compoundIncludeLabels: { enums: ['include', 'exclude'] }, + arrowShape: { enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'half-triangle-overshot', 'vee', 'square', 'circle', 'diamond', 'none'] }, + arrowFill: { enums: ['filled', 'hollow'] }, + display: { enums: ['element', 'none'] }, + visibility: { enums: ['hidden', 'visible'] }, + zCompoundDepth: { enums: ['bottom', 'orphan', 'auto', 'top'] }, + zIndexCompare: { enums: ['auto', 'manual'] }, + valign: { enums: ['top', 'center', 'bottom'] }, + halign: { enums: ['left', 'center', 'right'] }, + text: { string: true }, + data: { mapping: true, regex: data('data') }, + layoutData: { mapping: true, regex: data('layoutData') }, + scratch: { mapping: true, regex: data('scratch') }, + mapData: { mapping: true, regex: mapData('mapData') }, + mapLayoutData: { mapping: true, regex: mapData('mapLayoutData') }, + mapScratch: { mapping: true, regex: mapData('mapScratch') }, + fn: { mapping: true, fn: true }, + url: { regexes: urlRegexes, singleRegexMatchValue: true }, + urls: { regexes: urlRegexes, singleRegexMatchValue: true, multiple: true }, + propList: { propList: true }, + angle: { number: true, units: 'deg|rad', implicitUnits: 'rad' }, + textRotation: { number: true, units: 'deg|rad', implicitUnits: 'rad', enums: ['none', 'autorotate'] }, + polygonPointList: { number: true, multiple: true, evenMultiple: true, min: -1, max: 1, unitless: true }, + edgeDistances: { enums: ['intersection', 'node-position'] }, + edgeEndpoint: { + number: true, multiple: true, units: '%|px|em|deg|rad', implicitUnits: 'px', + enums: ['inside-to-node', 'outside-to-node', 'outside-to-line'], singleEnum: true, + validate: function validate(valArr, unitsArr) { + switch (valArr.length) { + case 2: + // can be % or px only + return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad'; + case 1: + // can be enum, deg, or rad only + return is.string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad'; + default: + return false; + } + } + }, + easing: { + regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'], + enums: ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'ease-in-sine', 'ease-out-sine', 'ease-in-out-sine', 'ease-in-quad', 'ease-out-quad', 'ease-in-out-quad', 'ease-in-cubic', 'ease-out-cubic', 'ease-in-out-cubic', 'ease-in-quart', 'ease-out-quart', 'ease-in-out-quart', 'ease-in-quint', 'ease-out-quint', 'ease-in-out-quint', 'ease-in-expo', 'ease-out-expo', 'ease-in-out-expo', 'ease-in-circ', 'ease-out-circ', 'ease-in-out-circ'] + } + }; - return { - x: xTranslated, - y: yTranslated - }; + var zOrderDiff = { + zeroNonZero: function zeroNonZero(val1, val2) { + if (val1 === 0 && val2 !== 0) { + return true; + } else if (val1 !== 0 && val2 === 0) { + return true; + } else { + return false; + } + }, + anyDiff: function anyDiff(val1, val2) { + return val1 !== val2; + } }; - var transformPoints = function transformPoints(pts, size, angle, translation) { - var retPts = []; + var zd = zOrderDiff; - for (var i = 0; i < pts.length; i += 2) { - var x = pts[i]; - var y = pts[i + 1]; + // define visual style properties + var t = styfn.types; + var props = styfn.properties = [ + // main label + { name: 'label', type: t.text }, { name: 'text-rotation', type: t.textRotation }, { name: 'text-margin-x', type: t.bidirectionalSize }, { name: 'text-margin-y', type: t.bidirectionalSize }, - retPts.push(transform(x, y, size, angle, translation)); - } + // source label + { name: 'source-label', type: t.text }, { name: 'source-text-rotation', type: t.textRotation }, { name: 'source-text-margin-x', type: t.bidirectionalSize }, { name: 'source-text-margin-y', type: t.bidirectionalSize }, { name: 'source-text-offset', type: t.size }, - return retPts; - }; + // target label + { name: 'target-label', type: t.text }, { name: 'target-text-rotation', type: t.textRotation }, { name: 'target-text-margin-x', type: t.bidirectionalSize }, { name: 'target-text-margin-y', type: t.bidirectionalSize }, { name: 'target-text-offset', type: t.size }, - var pointsToArr = function pointsToArr(pts) { - var ret = []; + // common label style + { name: 'text-valign', type: t.valign }, { name: 'text-halign', type: t.halign }, { name: 'color', type: t.color }, { name: 'text-outline-color', type: t.color }, { name: 'text-outline-width', type: t.size }, { name: 'text-outline-opacity', type: t.zeroOneNumber }, { name: 'text-opacity', type: t.zeroOneNumber }, { name: 'text-background-color', type: t.color }, { name: 'text-background-opacity', type: t.zeroOneNumber }, { name: 'text-background-padding', type: t.size }, { name: 'text-border-opacity', type: t.zeroOneNumber }, { name: 'text-border-color', type: t.color }, { name: 'text-border-width', type: t.size }, { name: 'text-border-style', type: t.borderStyle }, { name: 'text-background-shape', type: t.textBackgroundShape }, + // { name: 'text-decoration', type: t.textDecoration }, // not supported in canvas + { name: 'text-transform', type: t.textTransform }, { name: 'text-wrap', type: t.textWrap }, { name: 'text-max-width', type: t.size }, { name: 'text-events', type: t.bool }, { name: 'font-family', type: t.fontFamily }, { name: 'font-style', type: t.fontStyle }, + // { name: 'font-letiant', type: t.fontletiant }, // not useful + { name: 'font-weight', type: t.fontWeight }, { name: 'font-size', type: t.size }, { name: 'min-zoomed-font-size', type: t.size }, - for (var i = 0; i < pts.length; i++) { - var p = pts[i]; + // behaviour + { name: 'events', type: t.bool }, - ret.push(p.x, p.y); - } + // visibility + { name: 'display', type: t.display, triggersZOrder: zd.anyDiff }, { name: 'visibility', type: t.visibility, triggersZOrder: zd.anyDiff }, { name: 'opacity', type: t.zeroOneNumber, triggersZOrder: zd.zeroNonZero }, { name: 'z-compound-depth', type: t.zCompoundDepth, triggersZOrder: zd.anyDiff }, { name: 'z-index-compare', type: t.zIndexCompare, triggersZOrder: zd.anyDiff }, { name: 'z-index', type: t.nonNegativeInt, triggersZOrder: zd.anyDiff }, - return ret; - }; + // overlays + { name: 'overlay-padding', type: t.size }, { name: 'overlay-color', type: t.color }, { name: 'overlay-opacity', type: t.zeroOneNumber }, - var standardGap = function standardGap(edge) { - return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2; - }; + // transition anis + { name: 'transition-property', type: t.propList }, { name: 'transition-duration', type: t.time }, { name: 'transition-delay', type: t.time }, { name: 'transition-timing-function', type: t.easing }, - var defineArrowShape = function defineArrowShape(name, defn) { - if (is.string(defn)) { - defn = arrowShapes[defn]; - } + // node body + { name: 'height', type: t.nodeSize }, { name: 'width', type: t.nodeSize }, { name: 'shape', type: t.nodeShape }, { name: 'shape-polygon-points', type: t.polygonPointList }, { name: 'background-color', type: t.color }, { name: 'background-opacity', type: t.zeroOneNumber }, { name: 'background-blacken', type: t.nOneOneNumber }, { name: 'padding', type: t.sizeMaybePercent }, { name: 'padding-relative-to', type: t.paddingRelativeTo }, - arrowShapes[name] = util.extend({ - name: name, + // node border + { name: 'border-color', type: t.color }, { name: 'border-opacity', type: t.zeroOneNumber }, { name: 'border-width', type: t.size }, { name: 'border-style', type: t.borderStyle }, - points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3], + // node background images + { name: 'background-image', type: t.urls }, { name: 'background-image-crossorigin', type: t.bgCrossOrigin }, { name: 'background-image-opacity', type: t.zeroOneNumbers }, { name: 'background-position-x', type: t.bgPos }, { name: 'background-position-y', type: t.bgPos }, { name: 'background-width-relative-to', type: t.bgRelativeTo }, { name: 'background-height-relative-to', type: t.bgRelativeTo }, { name: 'background-repeat', type: t.bgRepeat }, { name: 'background-fit', type: t.bgFit }, { name: 'background-clip', type: t.bgClip }, { name: 'background-width', type: t.bgWH }, { name: 'background-height', type: t.bgWH }, - collide: function collide(x, y, size, angle, translation, padding) { - var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation)); - var inside = math.pointInsidePolygonPoints(x, y, points); + // compound props + { name: 'position', type: t.position }, { name: 'compound-sizing-wrt-labels', type: t.compoundIncludeLabels }, { name: 'min-width', type: t.size }, { name: 'min-width-bias-left', type: t.sizeMaybePercent }, { name: 'min-width-bias-right', type: t.sizeMaybePercent }, { name: 'min-height', type: t.size }, { name: 'min-height-bias-top', type: t.sizeMaybePercent }, { name: 'min-height-bias-bottom', type: t.sizeMaybePercent }, - return inside; - }, + // edge line + { name: 'line-style', type: t.lineStyle }, { name: 'line-color', type: t.color }, { name: 'curve-style', type: t.curveStyle }, { name: 'haystack-radius', type: t.zeroOneNumber }, { name: 'source-endpoint', type: t.edgeEndpoint }, { name: 'target-endpoint', type: t.edgeEndpoint }, { name: 'control-point-step-size', type: t.size }, { name: 'control-point-distances', type: t.bidirectionalSizes }, { name: 'control-point-weights', type: t.numbers }, { name: 'segment-distances', type: t.bidirectionalSizes }, { name: 'segment-weights', type: t.numbers }, { name: 'edge-distances', type: t.edgeDistances }, { name: 'arrow-scale', type: t.positiveNumber }, { name: 'loop-direction', type: t.angle }, { name: 'loop-sweep', type: t.angle }, { name: 'source-distance-from-node', type: t.size }, { name: 'target-distance-from-node', type: t.size }, - roughCollide: bbCollide, + // ghost properties + { name: 'ghost', type: t.bool }, { name: 'ghost-offset-x', type: t.bidirectionalSize }, { name: 'ghost-offset-y', type: t.bidirectionalSize }, { name: 'ghost-opacity', type: t.zeroOneNumber }, - draw: function draw(context, size, angle, translation) { - var points = transformPoints(this.points, size, angle, translation); + // these are just for the core + { name: 'selection-box-color', type: t.color }, { name: 'selection-box-opacity', type: t.zeroOneNumber }, { name: 'selection-box-border-color', type: t.color }, { name: 'selection-box-border-width', type: t.size }, { name: 'active-bg-color', type: t.color }, { name: 'active-bg-opacity', type: t.zeroOneNumber }, { name: 'active-bg-size', type: t.size }, { name: 'outside-texture-bg-color', type: t.color }, { name: 'outside-texture-bg-opacity', type: t.zeroOneNumber }]; - renderer.arrowShapeImpl('polygon')(context, points); - }, + // define aliases + var aliases = styfn.aliases = [{ name: 'content', pointsTo: 'label' }, { name: 'control-point-distance', pointsTo: 'control-point-distances' }, { name: 'control-point-weight', pointsTo: 'control-point-weights' }, { name: 'edge-text-rotation', pointsTo: 'text-rotation' }, { name: 'padding-left', pointsTo: 'padding' }, { name: 'padding-right', pointsTo: 'padding' }, { name: 'padding-top', pointsTo: 'padding' }, { name: 'padding-bottom', pointsTo: 'padding' }]; - spacing: function spacing(edge) { - return 0; - }, + // pie backgrounds for nodes + styfn.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use) + props.push({ name: 'pie-size', type: t.sizeMaybePercent }); + for (var i = 1; i <= styfn.pieBackgroundN; i++) { + props.push({ name: 'pie-' + i + '-background-color', type: t.color }); + props.push({ name: 'pie-' + i + '-background-size', type: t.percent }); + props.push({ name: 'pie-' + i + '-background-opacity', type: t.zeroOneNumber }); + } - gap: standardGap - }, defn); - }; + // edge arrows + var arrowPrefixes = styfn.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target']; + [{ name: 'arrow-shape', type: t.arrowShape }, { name: 'arrow-color', type: t.color }, { name: 'arrow-fill', type: t.arrowFill }].forEach(function (prop) { + arrowPrefixes.forEach(function (prefix) { + var name = prefix + '-' + prop.name; + var type = prop.type; - defineArrowShape('none', { - collide: util.falsify, + props.push({ name: name, type: type }); + }); + }, {}); - roughCollide: util.falsify, + // list of property names + styfn.propertyNames = props.map(function (p) { + return p.name; + }); - draw: util.noop, + // allow access of properties by name ( e.g. style.properties.height ) + for (var _i = 0; _i < props.length; _i++) { + var prop = props[_i]; - spacing: util.zeroify, + props[prop.name] = prop; // allow lookup by name + } - gap: util.zeroify - }); + // map aliases + for (var _i2 = 0; _i2 < aliases.length; _i2++) { + var alias = aliases[_i2]; + var pointsToProp = props[alias.pointsTo]; + var aliasProp = { + name: alias.name, + alias: true, + pointsTo: pointsToProp + }; - defineArrowShape('triangle', { - points: [-0.15, -0.3, 0, 0, 0.15, -0.3] - }); + // add alias prop for parsing + props.push(aliasProp); - defineArrowShape('arrow', 'triangle'); + props[alias.name] = aliasProp; // allow lookup by name + } +})(); - defineArrowShape('triangle-backcurve', { - points: arrowShapes['triangle'].points, +styfn.getDefaultProperty = function (name) { + return this.getDefaultProperties()[name]; +}; - controlPoint: [0, -0.15], +styfn.getDefaultProperties = util.memoize(function () { + var rawProps = util.extend({ + // common node/edge props + 'events': 'yes', + 'text-events': 'no', + 'text-valign': 'top', + 'text-halign': 'center', + 'color': '#000', + 'text-outline-color': '#000', + 'text-outline-width': 0, + 'text-outline-opacity': 1, + 'text-opacity': 1, + 'text-decoration': 'none', + 'text-transform': 'none', + 'text-wrap': 'none', + 'text-max-width': 9999, + 'text-background-color': '#000', + 'text-background-opacity': 0, + 'text-background-shape': 'rectangle', + 'text-background-padding': 0, + 'text-border-opacity': 0, + 'text-border-width': 0, + 'text-border-style': 'solid', + 'text-border-color': '#000', + 'font-family': 'Helvetica Neue, Helvetica, sans-serif', + 'font-style': 'normal', + // 'font-letiant': fontletiant, + 'font-weight': 'normal', + 'font-size': 16, + 'min-zoomed-font-size': 0, + 'text-rotation': 'none', + 'source-text-rotation': 'none', + 'target-text-rotation': 'none', + 'visibility': 'visible', + 'display': 'element', + 'opacity': 1, + 'z-compound-depth': 'auto', + 'z-index-compare': 'auto', + 'z-index': 0, + 'label': '', + 'text-margin-x': 0, + 'text-margin-y': 0, + 'source-label': '', + 'source-text-offset': 0, + 'source-text-margin-x': 0, + 'source-text-margin-y': 0, + 'target-label': '', + 'target-text-offset': 0, + 'target-text-margin-x': 0, + 'target-text-margin-y': 0, + 'overlay-opacity': 0, + 'overlay-color': '#000', + 'overlay-padding': 10, + 'transition-property': 'none', + 'transition-duration': 0, + 'transition-delay': 0, + 'transition-timing-function': 'linear', - roughCollide: bbCollide, + // node props + 'background-blacken': 0, + 'background-color': '#999', + 'background-opacity': 1, + 'background-image': 'none', + 'background-image-crossorigin': 'anonymous', + 'background-image-opacity': 1, + 'background-position-x': '50%', + 'background-position-y': '50%', + 'background-width-relative-to': 'include-padding', + 'background-height-relative-to': 'include-padding', + 'background-repeat': 'no-repeat', + 'background-fit': 'none', + 'background-clip': 'node', + 'background-width': 'auto', + 'background-height': 'auto', + 'border-color': '#000', + 'border-opacity': 1, + 'border-width': 0, + 'border-style': 'solid', + 'height': 30, + 'width': 30, + 'shape': 'ellipse', + 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1', - draw: function draw(context, size, angle, translation, edgeWidth) { - var ptsTrans = transformPoints(this.points, size, angle, translation); - var ctrlPt = this.controlPoint; - var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation); + // ghost props + 'ghost': 'no', + 'ghost-offset-y': 0, + 'ghost-offset-x': 0, + 'ghost-opacity': 0, - renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans); - }, + // compound props + 'padding': 0, + 'padding-relative-to': 'width', + 'position': 'origin', + 'compound-sizing-wrt-labels': 'include', + 'min-width': 0, + 'min-width-bias-left': 0, + 'min-width-bias-right': 0, + 'min-height': 0, + 'min-height-bias-top': 0, + 'min-height-bias-bottom': 0 + }, { + // node pie bg + 'pie-size': '100%' + }, [{ name: 'pie-{{i}}-background-color', value: 'black' }, { name: 'pie-{{i}}-background-size', value: '0%' }, { name: 'pie-{{i}}-background-opacity', value: 1 }].reduce(function (css, prop) { + for (var i = 1; i <= styfn.pieBackgroundN; i++) { + var name = prop.name.replace('{{i}}', i); + var val = prop.value; - gap: function gap(edge) { - return standardGap(edge) * 0.985; + css[name] = val; } - }); - - defineArrowShape('triangle-tee', { - points: [-0.15, -0.3, 0, 0, 0.15, -0.3, -0.15, -0.3], - pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4], + return css; + }, {}), { + // edge props + 'line-style': 'solid', + 'line-color': '#999', + 'control-point-step-size': 40, + 'control-point-weights': 0.5, + 'segment-weights': 0.5, + 'segment-distances': 20, + 'edge-distances': 'intersection', + 'curve-style': 'bezier', + 'haystack-radius': 0, + 'arrow-scale': 1, + 'loop-direction': '-45deg', + 'loop-sweep': '-90deg', + 'source-distance-from-node': 0, + 'target-distance-from-node': 0, + 'source-endpoint': 'outside-to-node', + 'target-endpoint': 'outside-to-node' + }, [{ name: 'arrow-shape', value: 'none' }, { name: 'arrow-color', value: '#999' }, { name: 'arrow-fill', value: 'filled' }].reduce(function (css, prop) { + styfn.arrowPrefixes.forEach(function (prefix) { + var name = prefix + '-' + prop.name; + var val = prop.value; - collide: function collide(x, y, size, angle, translation, edgeWidth, padding) { - var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation)); - var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation)); + css[name] = val; + }); - var inside = math.pointInsidePolygonPoints(x, y, triPts) || math.pointInsidePolygonPoints(x, y, teePts); + return css; + }, {})); - return inside; - }, + var parsedProps = {}; - draw: function draw(context, size, angle, translation, edgeWidth) { - var triPts = transformPoints(this.points, size, angle, translation); - var teePts = transformPoints(this.pointsTee, size, angle, translation); + for (var i = 0; i < this.properties.length; i++) { + var prop = this.properties[i]; - renderer.arrowShapeImpl(this.name)(context, triPts, teePts); + if (prop.pointsTo) { + continue; } - }); - defineArrowShape('triangle-cross', { - points: [-0.15, -0.3, 0, 0, 0.15, -0.3, -0.15, -0.3], + var name = prop.name; + var val = rawProps[name]; + var parsedProp = this.parse(name, val); + + parsedProps[name] = parsedProp; + } + + return parsedProps; +}); + +styfn.addDefaultStylesheet = function () { + this.selector('$node > node') // compound (parent) node properties + .css({ + 'shape': 'rectangle', + 'padding': 10, + 'background-color': '#eee', + 'border-color': '#ccc', + 'border-width': 1 + }).selector('edge') // just edge properties + .css({ + 'width': 3, + 'curve-style': 'haystack' + }).selector(':parent <-> node').css({ + 'curve-style': 'bezier', + 'source-endpoint': 'outside-to-line', + 'target-endpoint': 'outside-to-line' + }).selector(':selected').css({ + 'background-color': '#0169D9', + 'line-color': '#0169D9', + 'source-arrow-color': '#0169D9', + 'target-arrow-color': '#0169D9', + 'mid-source-arrow-color': '#0169D9', + 'mid-target-arrow-color': '#0169D9' + }).selector('node:parent:selected').css({ + 'background-color': '#CCE1F9', + 'border-color': '#aec8e5' + }).selector(':active').css({ + 'overlay-color': 'black', + 'overlay-padding': 10, + 'overlay-opacity': 0.25 + }).selector('core') // just core properties + .css({ + 'selection-box-color': '#ddd', + 'selection-box-opacity': 0.65, + 'selection-box-border-color': '#aaa', + 'selection-box-border-width': 1, + 'active-bg-color': 'black', + 'active-bg-opacity': 0.15, + 'active-bg-size': 30, + 'outside-texture-bg-color': '#000', + 'outside-texture-bg-opacity': 0.125 + }); - baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle - -0.15, -0.4, 0.15, -0.4, // second half of the rectangle - 0.15, -0.4], + this.defaultLength = this.length; +}; - crossLinePts: function crossLinePts(size, edgeWidth) { - // shift points so that the distance between the cross points matches edge width - var p = this.baseCrossLinePts.slice(); - var shiftFactor = edgeWidth / size; - var y0 = 3; - var y1 = 5; +module.exports = styfn; - p[y0] = p[y0] - shiftFactor; - p[y1] = p[y1] - shiftFactor; +/***/ }), +/* 92 */ +/***/ (function(module, exports, __webpack_require__) { - return p; - }, +"use strict"; - collide: function collide(x, y, size, angle, translation, edgeWidth, padding) { - var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation)); - var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation)); - var inside = math.pointInsidePolygonPoints(x, y, triPts) || math.pointInsidePolygonPoints(x, y, teePts); - return inside; - }, +var util = __webpack_require__(1); +var is = __webpack_require__(0); +var math = __webpack_require__(2); - draw: function draw(context, size, angle, translation, edgeWidth) { - var triPts = transformPoints(this.points, size, angle, translation); - var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation); +var styfn = {}; - renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts); - } - }); +// a caching layer for property parsing +styfn.parse = function (name, value, propIsBypass, propIsFlat) { + var self = this; - defineArrowShape('vee', { - points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15], + // function values can't be cached in all cases, and there isn't much benefit of caching them anyway + if (is.fn(value)) { + return self.parseImplWarn(name, value, propIsBypass, propIsFlat); + } - gap: function gap(edge) { - return standardGap(edge) * 0.985; - } - }); + var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat; + var argHash = [name, value, propIsBypass, flatKey].join('$'); + var propCache = self.propCache = self.propCache || {}; + var ret = void 0; - defineArrowShape('circle', { - radius: 0.15, + if (!(ret = propCache[argHash])) { + ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat); + } - collide: function collide(x, y, size, angle, translation, edgeWidth, padding) { - var t = translation; - var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2); + // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden + // - mappings can't be shared b/c mappings are per-element + if (propIsBypass || propIsFlat === 'mapping') { + // need a copy since props are mutated later in their lifecycles + ret = util.copy(ret); - return inside; - }, + if (ret) { + ret.value = util.copy(ret.value); // because it could be an array, e.g. colour + } + } - draw: function draw(context, size, angle, translation, edgeWidth) { - renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size); - }, + return ret; +}; - spacing: function spacing(edge) { - return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius; - } - }); +styfn.parseImplWarn = function (name, value, propIsBypass, propIsFlat) { + var prop = this.parseImpl(name, value, propIsBypass, propIsFlat); - defineArrowShape('tee', { - points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0], + if (!prop && value != null) { + util.error('The style property `%s: %s` is invalid', name, value); + } - spacing: function spacing(edge) { - return 1; - }, + return prop; +}; - gap: function gap(edge) { - return 1; - } - }); +// parse a property; return null on invalid; return parsed property otherwise +// fields : +// - name : the name of the property +// - value : the parsed, native-typed value of the property +// - strValue : a string value that represents the property value in valid css +// - bypass : true iff the property is a bypass property +styfn.parseImpl = function (name, value, propIsBypass, propIsFlat) { + var self = this; - defineArrowShape('square', { - points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3] - }); + name = util.camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName') - defineArrowShape('diamond', { - points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0], + var property = self.properties[name]; + var passedValue = value; + var types = self.types; - gap: function gap(edge) { - return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value; - } - }); -}; + if (!property) { + return null; + } // return null on property of unknown name + if (value === undefined || value === null) { + return null; + } // can't assign null -module.exports = BRp; + // the property may be an alias + if (property.alias) { + property = property.pointsTo; + name = property.name; + } -/***/ }), -/* 86 */ -/***/ (function(module, exports, __webpack_require__) { + var valueIsString = is.string(value); + if (valueIsString) { + // trim the value to make parsing easier + value = value.trim(); + } -"use strict"; + var type = property.type; + if (!type) { + return null; + } // no type, no luck + // check if bypass is null or empty string (i.e. indication to delete bypass property) + if (propIsBypass && (value === '' || value === null)) { + return { + name: name, + value: value, + bypass: true, + deleteBypass: true + }; + } -var window = __webpack_require__(4); -var math = __webpack_require__(2); -var util = __webpack_require__(1); -var window = __webpack_require__(4); + // check if value is a function used as a mapper + if (is.fn(value)) { + return { + name: name, + value: value, + strValue: 'fn', + mapped: types.fn, + bypass: propIsBypass + }; + } -var BRp = {}; + // check if value is mapped + var data = void 0, + mapData = void 0; + if (!valueIsString || propIsFlat) { + // then don't bother to do the expensive regex checks -// Project mouse -BRp.projectIntoViewport = function (clientX, clientY) { - var cy = this.cy; - var offsets = this.findContainerClientCoords(); - var offsetLeft = offsets[0]; - var offsetTop = offsets[1]; - var scale = offsets[4]; - var pan = cy.pan(); - var zoom = cy.zoom(); + } else if (data = new RegExp(types.data.regex).exec(value)) { + if (propIsBypass) { + return false; + } // mappers not allowed in bypass - var x = ((clientX - offsetLeft) / scale - pan.x) / zoom; - var y = ((clientY - offsetTop) / scale - pan.y) / zoom; + var mapped = types.data; - return [x, y]; -}; + return { + name: name, + value: data, + strValue: '' + value, + mapped: mapped, + field: data[1], + bypass: propIsBypass + }; + } else if (mapData = new RegExp(types.mapData.regex).exec(value)) { + if (propIsBypass) { + return false; + } // mappers not allowed in bypass + if (type.multiple) { + return false; + } // impossible to map to num -BRp.findContainerClientCoords = function () { - if (this.containerBB) { - return this.containerBB; - } + var _mapped = types.mapData; - var container = this.container; - var rect = container.getBoundingClientRect(); - var style = window.getComputedStyle(container); - var styleValue = function styleValue(name) { - return parseFloat(style.getPropertyValue(name)); - }; + // we can map only if the type is a colour or a number + if (!(type.color || type.number)) { + return false; + } - var padding = { - left: styleValue('padding-left'), - right: styleValue('padding-right'), - top: styleValue('padding-top'), - bottom: styleValue('padding-bottom') - }; + var valueMin = this.parse(name, mapData[4]); // parse to validate + if (!valueMin || valueMin.mapped) { + return false; + } // can't be invalid or mapped - var border = { - left: styleValue('border-left-width'), - right: styleValue('border-right-width'), - top: styleValue('border-top-width'), - bottom: styleValue('border-bottom-width') - }; + var valueMax = this.parse(name, mapData[5]); // parse to validate + if (!valueMax || valueMax.mapped) { + return false; + } // can't be invalid or mapped - var clientWidth = container.clientWidth; - var clientHeight = container.clientHeight; + // check if valueMin and valueMax are the same + if (valueMin.value === valueMax.value) { + return false; // can't make much of a mapper without a range + } else if (type.color) { + var c1 = valueMin.value; + var c2 = valueMax.value; - var paddingHor = padding.left + padding.right; - var paddingVer = padding.top + padding.bottom; + var same = c1[0] === c2[0] // red + && c1[1] === c2[1] // green + && c1[2] === c2[2] // blue + && ( // optional alpha + c1[3] === c2[3] // same alpha outright + || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1? + c2[3] == null || c2[3] === 1) // full opacity for colour 2? + ); - var borderHor = border.left + border.right; - var borderVer = border.top + border.bottom; + if (same) { + return false; + } // can't make a mapper without a range + } - var scale = rect.width / (clientWidth + borderHor); + return { + name: name, + value: mapData, + strValue: '' + value, + mapped: _mapped, + field: mapData[1], + fieldMin: parseFloat(mapData[2]), // min & max are numeric + fieldMax: parseFloat(mapData[3]), + valueMin: valueMin.value, + valueMax: valueMax.value, + bypass: propIsBypass + }; + } - var unscaledW = clientWidth - paddingHor; - var unscaledH = clientHeight - paddingVer; + if (type.multiple && propIsFlat !== 'multiple') { + var vals = void 0; - var scaledW = rect.width - (paddingHor + borderHor) * scale; - var scaledH = rect.height - (paddingVer + borderVer) * scale; + if (valueIsString) { + vals = value.split(/\s+/); + } else if (is.array(value)) { + vals = value; + } else { + vals = [value]; + } - var left = rect.left + padding.left + border.left; - var top = rect.top + padding.top + border.top; + if (type.evenMultiple && vals.length % 2 !== 0) { + return null; + } - return this.containerBB = [left, top, unscaledW, unscaledH, scale]; -}; + var valArr = []; + var unitsArr = []; + var pfValArr = []; + var hasEnum = false; -BRp.invalidateContainerClientCoordsCache = function () { - this.containerBB = null; -}; + for (var i = 0; i < vals.length; i++) { + var p = self.parse(name, vals[i], propIsBypass, 'multiple'); -BRp.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) { - return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0]; -}; + hasEnum = hasEnum || is.string(p.value); -BRp.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) { - var self = this; - var r = this; - var eles = r.getCachedZSortedEles(); - var near = []; // 1 node max, 1 edge max - var zoom = r.cy.zoom(); - var hasCompounds = r.cy.hasCompoundNodes(); - var edgeThreshold = (isTouch ? 24 : 8) / zoom; - var nodeThreshold = (isTouch ? 8 : 2) / zoom; - var labelThreshold = (isTouch ? 8 : 2) / zoom; - var minSqDist = Infinity; - var nearEdge; - var nearNode; + valArr.push(p.value); + pfValArr.push(p.pfValue != null ? p.pfValue : p.value); + unitsArr.push(p.units); + } - if (interactiveElementsOnly) { - eles = eles.interactive; - } + if (type.validate && !type.validate(valArr, unitsArr)) { + return null; + } - function addEle(ele, sqDist) { - if (ele.isNode()) { - if (nearNode) { - return; // can't replace node + if (type.singleEnum && hasEnum) { + if (valArr.length === 1 && is.string(valArr[0])) { + return { + name: name, + value: valArr[0], + strValue: valArr[0], + bypass: propIsBypass + }; } else { - nearNode = ele; - near.push(ele); + return null; } } - if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) { - if (nearEdge) { - // then replace existing edge - // can replace only if same z-index - if (nearEdge.pstyle('z-index').value === ele.pstyle('z-index').value) { - for (var i = 0; i < near.length; i++) { - if (near[i].isEdge()) { - near[i] = ele; - nearEdge = ele; - minSqDist = sqDist != null ? sqDist : minSqDist; - break; - } - } - } - } else { - near.push(ele); - nearEdge = ele; - minSqDist = sqDist != null ? sqDist : minSqDist; + return { + name: name, + value: valArr, + pfValue: pfValArr, + strValue: valArr.join(' '), + bypass: propIsBypass, + units: unitsArr + }; + } + + // several types also allow enums + var checkEnums = function checkEnums() { + for (var _i = 0; _i < type.enums.length; _i++) { + var en = type.enums[_i]; + + if (en === value) { + return { + name: name, + value: value, + strValue: '' + value, + bypass: propIsBypass + }; } } - } - function checkNode(node) { - var width = node.outerWidth() + 2 * nodeThreshold; - var height = node.outerHeight() + 2 * nodeThreshold; - var hw = width / 2; - var hh = height / 2; - var pos = node.position(); + return null; + }; - if (pos.x - hw <= x && x <= pos.x + hw // bb check x - && pos.y - hh <= y && y <= pos.y + hh // bb check y - ) { - var shape = r.nodeShapes[self.getNodeShape(node)]; + // check the type and return the appropriate object + if (type.number) { + var units = void 0; + var implicitUnits = 'px'; // not set => px - if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) { - addEle(node, 0); - return true; + if (type.units) { + // use specified units if set + units = type.units; + } + + if (type.implicitUnits) { + implicitUnits = type.implicitUnits; + } + + if (!type.unitless) { + if (valueIsString) { + var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : ''); + if (units) { + unitsRegex = units; + } // only allow explicit units if so set + var match = value.match('^(' + util.regex.number + ')(' + unitsRegex + ')?' + '$'); + + if (match) { + value = match[1]; + units = match[2] || implicitUnits; } + } else if (!units || type.implicitUnits) { + units = implicitUnits; // implicitly px if unspecified } - } + } - function checkEdge(edge) { - var _p = edge._private; + value = parseFloat(value); - var rs = _p.rscratch; - var styleWidth = edge.pstyle('width').pfValue; - var scale = edge.pstyle('arrow-scale').value; - var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre - var widthSq = width * width; - var width2 = width * 2; - var src = _p.source; - var tgt = _p.target; - var inEdgeBB = false; - var sqDist; + // if not a number and enums not allowed, then the value is invalid + if (isNaN(value) && type.enums === undefined) { + return null; + } - if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') { - var pts = rs.allpts; + // check if this number type also accepts special keywords in place of numbers + // (i.e. `left`, `auto`, etc) + if (isNaN(value) && type.enums !== undefined) { + value = passedValue; - for (var i = 0; i + 3 < pts.length; i += 2) { - if ((inEdgeBB = math.inLineVicinity(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], width2)) && widthSq > (sqDist = math.sqdistToFiniteLine(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3]))) { - addEle(edge, sqDist); - return true; - } - } - } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') { - var pts = rs.allpts; - for (var i = 0; i + 5 < rs.allpts.length; i += 4) { - if ((inEdgeBB = math.inBezierVicinity(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5], width2)) && widthSq > (sqDist = math.sqdistToQuadraticBezier(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5]))) { - addEle(edge, sqDist); - return true; - } - } + return checkEnums(); } - // if we're close to the edge but didn't hit it, maybe we hit its arrows + // check if value must be an integer + if (type.integer && !is.integer(value)) { + return null; + } - var src = src || _p.source; - var tgt = tgt || _p.target; + // check value is within range + if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) { + return null; + } - var arSize = self.getArrowWidth(styleWidth, scale); + var ret = { + name: name, + value: value, + strValue: '' + value + (units ? units : ''), + units: units, + bypass: propIsBypass + }; - var arrows = [{ name: 'source', x: rs.arrowStartX, y: rs.arrowStartY, angle: rs.srcArrowAngle }, { name: 'target', x: rs.arrowEndX, y: rs.arrowEndY, angle: rs.tgtArrowAngle }, { name: 'mid-source', x: rs.midX, y: rs.midY, angle: rs.midsrcArrowAngle }, { name: 'mid-target', x: rs.midX, y: rs.midY, angle: rs.midtgtArrowAngle }]; + // normalise value in pixels + if (type.unitless || units !== 'px' && units !== 'em') { + ret.pfValue = value; + } else { + ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value; + } - for (var i = 0; i < arrows.length; i++) { - var ar = arrows[i]; - var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value]; - var edgeWidth = edge.pstyle('width').pfValue; - if (shape.roughCollide(x, y, arSize, ar.angle, { x: ar.x, y: ar.y }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, { x: ar.x, y: ar.y }, edgeWidth, edgeThreshold)) { - addEle(edge); - return true; - } + // normalise value in ms + if (units === 'ms' || units === 's') { + ret.pfValue = units === 'ms' ? value : 1000 * value; + } + + // normalise value in rad + if (units === 'deg' || units === 'rad') { + ret.pfValue = units === 'rad' ? value : math.deg2rad(value); } - // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence) - if (hasCompounds && near.length > 0) { - checkNode(src); - checkNode(tgt); + // normalize value in % + if (units === '%') { + ret.pfValue = value / 100; } - } - function preprop(obj, name, pre) { - return util.getPrefixedProperty(obj, name, pre); - } + return ret; + } else if (type.propList) { - function checkLabel(ele, prefix) { - var _p = ele._private; - var th = labelThreshold; + var props = []; + var propsStr = '' + value; + + if (propsStr === 'none') { + // leave empty - var prefixDash; - if (prefix) { - prefixDash = prefix + '-'; } else { - prefixDash = ''; + // go over each prop + + var propsSplit = propsStr.split(','); + for (var _i2 = 0; _i2 < propsSplit.length; _i2++) { + var propName = propsSplit[_i2].trim(); + + if (self.properties[propName]) { + props.push(propName); + } + } + + if (props.length === 0) { + return null; + } } - var text = ele.pstyle(prefixDash + 'label').value; - var eventsEnabled = ele.pstyle('text-events').strValue === 'yes'; + return { + name: name, + value: props, + strValue: props.length === 0 ? 'none' : props.join(', '), + bypass: propIsBypass + }; + } else if (type.color) { + var tuple = util.color2tuple(value); - if (!eventsEnabled || !text) { - return; + if (!tuple) { + return null; } - var rstyle = _p.rstyle; - var bw = ele.pstyle('text-border-width').pfValue; - var pw = ele.pstyle('text-background-padding').pfValue; - var lw = preprop(rstyle, 'labelWidth', prefix) + bw + 2 * th + 2 * pw; - var lh = preprop(rstyle, 'labelHeight', prefix) + bw + 2 * th + 2 * pw; - var lx = preprop(rstyle, 'labelX', prefix); - var ly = preprop(rstyle, 'labelY', prefix); + return { + name: name, + value: tuple, + pfValue: tuple, + strValue: '' + value, + bypass: propIsBypass + }; + } else if (type.regex || type.regexes) { - var theta = preprop(_p.rscratch, 'labelAngle', prefix); + // first check enums + if (type.enums) { + var enumProp = checkEnums(); - var lx1 = lx - lw / 2; - var lx2 = lx + lw / 2; - var ly1 = ly - lh / 2; - var ly2 = ly + lh / 2; + if (enumProp) { + return enumProp; + } + } - if (theta) { - var cos = Math.cos(theta); - var sin = Math.sin(theta); + var regexes = type.regexes ? type.regexes : [type.regex]; - var rotate = function rotate(x, y) { - x = x - lx; - y = y - ly; + for (var _i3 = 0; _i3 < regexes.length; _i3++) { + var regex = new RegExp(regexes[_i3]); // make a regex from the type string + var m = regex.exec(value); + if (m) { + // regex matches return { - x: x * cos - y * sin + lx, - y: x * sin + y * cos + ly + name: name, + value: type.singleRegexMatchValue ? m[1] : m, + strValue: '' + value, + bypass: propIsBypass }; - }; - - var px1y1 = rotate(lx1, ly1); - var px1y2 = rotate(lx1, ly2); - var px2y1 = rotate(lx2, ly1); - var px2y2 = rotate(lx2, ly2); - - var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y]; - - if (math.pointInsidePolygonPoints(x, y, points)) { - addEle(ele); - return true; - } - } else { - // do a cheaper bb check - var bb = { - w: lw, - h: lh, - x1: lx1, - x2: lx2, - y1: ly1, - y2: ly2 - }; - - if (math.inBoundingBox(bb, x, y)) { - addEle(ele); - return true; } } - } - - for (var i = eles.length - 1; i >= 0; i--) { - // reverse order for precedence - var ele = eles[i]; - if (ele.isNode()) { - checkNode(ele) || checkLabel(ele); - } else { - // then edge - checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target'); - } + return null; // didn't match any + } else if (type.string) { + // just return + return { + name: name, + value: '' + value, + strValue: '' + value, + bypass: propIsBypass + }; + } else if (type.enums) { + // check enums last because it's a combo type in others + return checkEnums(); + } else { + return null; // not a type we can handle } - - return near; }; -// 'Give me everything from this box' -BRp.getAllInBox = function (x1, y1, x2, y2) { - var eles = this.getCachedZSortedEles().interactive; - var box = []; +module.exports = styfn; - var x1c = Math.min(x1, x2); - var x2c = Math.max(x1, x2); - var y1c = Math.min(y1, y2); - var y2c = Math.max(y1, y2); +/***/ }), +/* 93 */ +/***/ (function(module, exports, __webpack_require__) { - x1 = x1c; - x2 = x2c; - y1 = y1c; - y2 = y2c; +"use strict"; - var boxBb = math.makeBoundingBox({ - x1: x1, y1: y1, - x2: x2, y2: y2 - }); - for (var e = 0; e < eles.length; e++) { - var ele = eles[e]; +var is = __webpack_require__(0); +var window = __webpack_require__(3); +var math = __webpack_require__(2); - if (ele.isNode()) { - var node = ele; - var nodeBb = node.boundingBox({ - includeNodes: true, - includeEdges: false, - includeLabels: false - }); +var corefn = { - if (math.boundingBoxesIntersect(boxBb, nodeBb) && !math.boundingBoxInBoundingBox(nodeBb, boxBb)) { - box.push(node); - } + autolock: function autolock(bool) { + if (bool !== undefined) { + this._private.autolock = bool ? true : false; } else { - var edge = ele; - var _p = edge._private; - var rs = _p.rscratch; + return this._private.autolock; + } - if (rs.startX != null && rs.startY != null && !math.inBoundingBox(boxBb, rs.startX, rs.startY)) { - continue; - } - if (rs.endX != null && rs.endY != null && !math.inBoundingBox(boxBb, rs.endX, rs.endY)) { - continue; - } + return this; // chaining + }, - if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') { + autoungrabify: function autoungrabify(bool) { + if (bool !== undefined) { + this._private.autoungrabify = bool ? true : false; + } else { + return this._private.autoungrabify; + } - var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts; - var allInside = true; + return this; // chaining + }, - for (var i = 0; i < pts.length; i++) { - if (!math.pointInBoundingBox(boxBb, pts[i])) { - allInside = false; - break; - } - } + autounselectify: function autounselectify(bool) { + if (bool !== undefined) { + this._private.autounselectify = bool ? true : false; + } else { + return this._private.autounselectify; + } - if (allInside) { - box.push(edge); - } - } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') { - box.push(edge); - } + return this; // chaining + }, + + panningEnabled: function panningEnabled(bool) { + if (bool !== undefined) { + this._private.panningEnabled = bool ? true : false; + } else { + return this._private.panningEnabled; } - } - return box; -}; + return this; // chaining + }, + + userPanningEnabled: function userPanningEnabled(bool) { + if (bool !== undefined) { + this._private.userPanningEnabled = bool ? true : false; + } else { + return this._private.userPanningEnabled; + } + + return this; // chaining + }, + + zoomingEnabled: function zoomingEnabled(bool) { + if (bool !== undefined) { + this._private.zoomingEnabled = bool ? true : false; + } else { + return this._private.zoomingEnabled; + } -module.exports = BRp; + return this; // chaining + }, -/***/ }), -/* 87 */ -/***/ (function(module, exports, __webpack_require__) { + userZoomingEnabled: function userZoomingEnabled(bool) { + if (bool !== undefined) { + this._private.userZoomingEnabled = bool ? true : false; + } else { + return this._private.userZoomingEnabled; + } -"use strict"; + return this; // chaining + }, + boxSelectionEnabled: function boxSelectionEnabled(bool) { + if (bool !== undefined) { + this._private.boxSelectionEnabled = bool ? true : false; + } else { + return this._private.boxSelectionEnabled; + } -var math = __webpack_require__(2); + return this; // chaining + }, -var BRp = {}; + pan: function pan() { + var args = arguments; + var pan = this._private.pan; + var dim = void 0, + val = void 0, + dims = void 0, + x = void 0, + y = void 0; -BRp.calculateArrowAngles = function (edge) { - var rs = edge._private.rscratch; - var isHaystack = rs.edgeType === 'haystack'; - var isBezier = rs.edgeType === 'bezier'; - var isMultibezier = rs.edgeType === 'multibezier'; - var isSegments = rs.edgeType === 'segments'; - var isCompound = rs.edgeType === 'compound'; - var isSelf = rs.edgeType === 'self'; + switch (args.length) { + case 0: + // .pan() + return pan; - // Displacement gives direction for arrowhead orientation - var dispX, dispY; - var startX, startY, endX, endY, midX, midY; + case 1: - if (isHaystack) { - startX = rs.haystackPts[0]; - startY = rs.haystackPts[1]; - endX = rs.haystackPts[2]; - endY = rs.haystackPts[3]; - } else { - startX = rs.arrowStartX; - startY = rs.arrowStartY; - endX = rs.arrowEndX; - endY = rs.arrowEndY; - } + if (is.string(args[0])) { + // .pan('x') + dim = args[0]; + return pan[dim]; + } else if (is.plainObject(args[0])) { + // .pan({ x: 0, y: 100 }) + if (!this._private.panningEnabled) { + return this; + } - midX = rs.midX; - midY = rs.midY; + dims = args[0]; + x = dims.x; + y = dims.y; - // source - // + if (is.number(x)) { + pan.x = x; + } - if (isSegments) { - dispX = startX - rs.segpts[0]; - dispY = startY - rs.segpts[1]; - } else if (isMultibezier || isCompound || isSelf || isBezier) { - var pts = rs.allpts; - var bX = math.qbezierAt(pts[0], pts[2], pts[4], 0.1); - var bY = math.qbezierAt(pts[1], pts[3], pts[5], 0.1); + if (is.number(y)) { + pan.y = y; + } - dispX = startX - bX; - dispY = startY - bY; - } else { - dispX = startX - midX; - dispY = startY - midY; - } + this.emit('pan viewport'); + } + break; - rs.srcArrowAngle = math.getAngleFromDisp(dispX, dispY); + case 2: + // .pan('x', 100) + if (!this._private.panningEnabled) { + return this; + } - // mid target - // + dim = args[0]; + val = args[1]; - var midX = rs.midX; - var midY = rs.midY; + if ((dim === 'x' || dim === 'y') && is.number(val)) { + pan[dim] = val; + } - if (isHaystack) { - midX = (startX + endX) / 2; - midY = (startY + endY) / 2; - } + this.emit('pan viewport'); + break; - dispX = endX - startX; - dispY = endY - startY; + default: + break; // invalid + } - if (isSegments) { - var pts = rs.allpts; + this.notify({ // notify the renderer that the viewport changed + type: 'viewport' + }); - if (pts.length / 2 % 2 === 0) { - var i2 = pts.length / 2; - var i1 = i2 - 2; + return this; // chaining + }, - dispX = pts[i2] - pts[i1]; - dispY = pts[i2 + 1] - pts[i1 + 1]; - } else { - var i2 = pts.length / 2 - 1; - var i1 = i2 - 2; - var i3 = i2 + 2; + panBy: function panBy(arg0, arg1) { + var args = arguments; + var pan = this._private.pan; + var dim = void 0, + val = void 0, + dims = void 0, + x = void 0, + y = void 0; - dispX = pts[i2] - pts[i1]; - dispY = pts[i2 + 1] - pts[i1 + 1]; + if (!this._private.panningEnabled) { + return this; } - } else if (isMultibezier || isCompound || isSelf) { - var pts = rs.allpts; - var cpts = rs.ctrlpts; - var bp0x, bp0y; - var bp1x, bp1y; - - if (cpts.length / 2 % 2 === 0) { - var p0 = pts.length / 2 - 1; // startpt - var ic = p0 + 2; - var p1 = ic + 2; - bp0x = math.qbezierAt(pts[p0], pts[ic], pts[p1], 0.0); - bp0y = math.qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0); + switch (args.length) { + case 1: - bp1x = math.qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001); - bp1y = math.qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001); - } else { - var ic = pts.length / 2 - 1; // ctrpt - var p0 = ic - 2; // startpt - var p1 = ic + 2; // endpt + if (is.plainObject(arg0)) { + // .panBy({ x: 0, y: 100 }) + dims = args[0]; + x = dims.x; + y = dims.y; - bp0x = math.qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999); - bp0y = math.qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999); + if (is.number(x)) { + pan.x += x; + } - bp1x = math.qbezierAt(pts[p0], pts[ic], pts[p1], 0.5); - bp1y = math.qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5); - } + if (is.number(y)) { + pan.y += y; + } - dispX = bp1x - bp0x; - dispY = bp1y - bp0y; - } + this.emit('pan viewport'); + } + break; - rs.midtgtArrowAngle = math.getAngleFromDisp(dispX, dispY); + case 2: + // .panBy('x', 100) + dim = arg0; + val = arg1; - rs.midDispX = dispX; - rs.midDispY = dispY; + if ((dim === 'x' || dim === 'y') && is.number(val)) { + pan[dim] += val; + } - // mid source - // + this.emit('pan viewport'); + break; - dispX *= -1; - dispY *= -1; + default: + break; // invalid + } - if (isSegments) { - var pts = rs.allpts; + this.notify({ // notify the renderer that the viewport changed + type: 'viewport' + }); - if (pts.length / 2 % 2 === 0) { - // already ok - } else { - var i2 = pts.length / 2 - 1; - var i3 = i2 + 2; + return this; // chaining + }, - dispX = -(pts[i3] - pts[i2]); - dispY = -(pts[i3 + 1] - pts[i2 + 1]); - } - } + fit: function fit(elements, padding) { + var viewportState = this.getFitViewport(elements, padding); - rs.midsrcArrowAngle = math.getAngleFromDisp(dispX, dispY); + if (viewportState) { + var _p = this._private; + _p.zoom = viewportState.zoom; + _p.pan = viewportState.pan; - // target - // + this.emit('pan zoom viewport'); - if (isSegments) { - dispX = endX - rs.segpts[rs.segpts.length - 2]; - dispY = endY - rs.segpts[rs.segpts.length - 1]; - } else if (isMultibezier || isCompound || isSelf || isBezier) { - var pts = rs.allpts; - var l = pts.length; - var bX = math.qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9); - var bY = math.qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9); + this.notify({ // notify the renderer that the viewport changed + type: 'viewport' + }); + } - dispX = endX - bX; - dispY = endY - bY; - } else { - dispX = endX - midX; - dispY = endY - midY; - } + return this; // chaining + }, - rs.tgtArrowAngle = math.getAngleFromDisp(dispX, dispY); -}; + getFitViewport: function getFitViewport(elements, padding) { + if (is.number(elements) && padding === undefined) { + // elements is optional + padding = elements; + elements = undefined; + } -BRp.getArrowWidth = BRp.getArrowHeight = function (edgeWidth, scale) { - var cache = this.arrowWidthCache = this.arrowWidthCache || {}; + if (!this._private.panningEnabled || !this._private.zoomingEnabled) { + return; + } - var cachedVal = cache[edgeWidth + ', ' + scale]; - if (cachedVal) { - return cachedVal; - } + var bb = void 0; - cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale; - cache[edgeWidth + ', ' + scale] = cachedVal; + if (is.string(elements)) { + var sel = elements; + elements = this.$(sel); + } else if (is.boundingBox(elements)) { + // assume bb + var bbe = elements; + bb = { + x1: bbe.x1, + y1: bbe.y1, + x2: bbe.x2, + y2: bbe.y2 + }; - return cachedVal; -}; + bb.w = bb.x2 - bb.x1; + bb.h = bb.y2 - bb.y1; + } else if (!is.elementOrCollection(elements)) { + elements = this.mutableElements(); + } -module.exports = BRp; + if (is.elementOrCollection(elements) && elements.empty()) { + return; + } // can't fit to nothing -/***/ }), -/* 88 */ -/***/ (function(module, exports, __webpack_require__) { + bb = bb || elements.boundingBox(); -"use strict"; + var w = this.width(); + var h = this.height(); + var zoom = void 0; + padding = is.number(padding) ? padding : 0; + + if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) { + zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); + // crop zoom + zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom; + zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom; -var math = __webpack_require__(2); -var is = __webpack_require__(0); + var pan = { // now pan to middle + x: (w - zoom * (bb.x1 + bb.x2)) / 2, + y: (h - zoom * (bb.y1 + bb.y2)) / 2 + }; -var BRp = {}; + return { + zoom: zoom, + pan: pan + }; + } -BRp.findEdgeControlPoints = function (edges) { - if (!edges || edges.length === 0) { return; - } + }, - var r = this; - var cy = r.cy; - var hasCompounds = cy.hasCompoundNodes(); - var hashTable = {}; - var pairIds = []; - var haystackEdges = []; + minZoom: function minZoom(zoom) { + if (zoom === undefined) { + return this._private.minZoom; + } else if (is.number(zoom)) { + this._private.minZoom = zoom; + } - // create a table of edge (src, tgt) => list of edges between them - var pairId; - for (var i = 0; i < edges.length; i++) { - var edge = edges[i]; - var _p = edge._private; - var data = _p.data; - var curveStyle = edge.pstyle('curve-style').value; - var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments'; - var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier'; + return this; + }, - // ignore edges who are not to be displayed - // they shouldn't take up space - if (edge.pstyle('display').value === 'none') { - continue; + maxZoom: function maxZoom(zoom) { + if (zoom === undefined) { + return this._private.maxZoom; + } else if (is.number(zoom)) { + this._private.maxZoom = zoom; } - if (curveStyle === 'haystack') { - haystackEdges.push(edge); - continue; + return this; + }, + + getZoomedViewport: function getZoomedViewport(params) { + var _p = this._private; + var currentPan = _p.pan; + var currentZoom = _p.zoom; + var pos = void 0; // in rendered px + var zoom = void 0; + var bail = false; + + if (!_p.zoomingEnabled) { + // zooming disabled + bail = true; } - var srcId = data.source; - var tgtId = data.target; + if (is.number(params)) { + // then set the zoom + zoom = params; + } else if (is.plainObject(params)) { + // then zoom about a point + zoom = params.level; - pairId = srcId > tgtId ? tgtId + '$-$' + srcId : srcId + '$-$' + tgtId; + if (params.position != null) { + pos = math.modelToRenderedPosition(params.position, currentZoom, currentPan); + } else if (params.renderedPosition != null) { + pos = params.renderedPosition; + } - if (edgeIsUnbundled) { - pairId = 'unbundled' + '$-$' + data.id; + if (pos != null && !_p.panningEnabled) { + // panning disabled + bail = true; + } } - var tableEntry = hashTable[pairId]; + // crop zoom + zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom; + zoom = zoom < _p.minZoom ? _p.minZoom : zoom; - if (tableEntry == null) { - tableEntry = hashTable[pairId] = []; - pairIds.push(pairId); + // can't zoom with invalid params + if (bail || !is.number(zoom) || zoom === currentZoom || pos != null && (!is.number(pos.x) || !is.number(pos.y))) { + return null; } - tableEntry.push(edge); + if (pos != null) { + // set zoom about position + var pan1 = currentPan; + var zoom1 = currentZoom; + var zoom2 = zoom; - if (edgeIsUnbundled) { - tableEntry.hasUnbundled = true; - } + var pan2 = { + x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x, + y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y + }; - if (edgeIsBezier) { - tableEntry.hasBezier = true; + return { + zoomed: true, + panned: true, + zoom: zoom2, + pan: pan2 + }; + } else { + // just set the zoom + return { + zoomed: true, + panned: false, + zoom: zoom, + pan: currentPan + }; } - } + }, - var src, tgt, srcPos, tgtPos, srcW, srcH, tgtW, tgtH, srcShape, tgtShape; - var vectorNormInverse; - var badBezier; + zoom: function zoom(params) { + if (params === undefined) { + // get + return this._private.zoom; + } else { + // set + var vp = this.getZoomedViewport(params); + var _p = this._private; - // for each pair (src, tgt), create the ctrl pts - // Nested for loop is OK; total number of iterations for both loops = edgeCount - for (var p = 0; p < pairIds.length; p++) { - pairId = pairIds[p]; - var pairEdges = hashTable[pairId]; + if (vp == null || !vp.zoomed) { + return this; + } - // for each pair id, the edges should be sorted by index - pairEdges.sort(function (edge1, edge2) { - return edge1.poolIndex() - edge2.poolIndex(); - }); + _p.zoom = vp.zoom; - src = pairEdges[0]._private.source; - tgt = pairEdges[0]._private.target; + if (vp.panned) { + _p.pan.x = vp.pan.x; + _p.pan.y = vp.pan.y; + } - // make sure src/tgt distinction is consistent for bundled edges - if (!pairEdges.hasUnbundled && src.id() > tgt.id()) { - var temp = src; - src = tgt; - tgt = temp; + this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport'); + + this.notify({ // notify the renderer that the viewport changed + type: 'viewport' + }); + + return this; // chaining + } + }, + + viewport: function viewport(opts) { + var _p = this._private; + var zoomDefd = true; + var panDefd = true; + var events = []; // to trigger + var zoomFailed = false; + var panFailed = false; + + if (!opts) { + return this; + } + if (!is.number(opts.zoom)) { + zoomDefd = false; + } + if (!is.plainObject(opts.pan)) { + panDefd = false; + } + if (!zoomDefd && !panDefd) { + return this; } - srcPos = src.position(); - tgtPos = tgt.position(); - - srcW = src.outerWidth(); - srcH = src.outerHeight(); - - tgtW = tgt.outerWidth(); - tgtH = tgt.outerHeight(); - - srcShape = r.nodeShapes[this.getNodeShape(src)]; - tgtShape = r.nodeShapes[this.getNodeShape(tgt)]; + if (zoomDefd) { + var z = opts.zoom; - badBezier = false; + if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) { + zoomFailed = true; + } else { + _p.zoom = z; - var edge; - var edge_p; - var rs; + events.push('zoom'); + } + } - var dirCounts = { - 'north': 0, - 'west': 0, - 'south': 0, - 'east': 0, - 'northwest': 0, - 'southwest': 0, - 'northeast': 0, - 'southeast': 0 - }; + if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) { + var p = opts.pan; - var srcX2 = srcPos.x; - var srcY2 = srcPos.y; - var srcW2 = srcW; - var srcH2 = srcH; + if (is.number(p.x)) { + _p.pan.x = p.x; + panFailed = false; + } - var tgtX2 = tgtPos.x; - var tgtY2 = tgtPos.y; - var tgtW2 = tgtW; - var tgtH2 = tgtH; + if (is.number(p.y)) { + _p.pan.y = p.y; + panFailed = false; + } - var numEdges2 = pairEdges.length; + if (!panFailed) { + events.push('pan'); + } + } - for (var i = 0; i < pairEdges.length; i++) { - edge = pairEdges[i]; - edge_p = edge._private; - rs = edge_p.rscratch; + if (events.length > 0) { + events.push('viewport'); + this.emit(events.join(' ')); - var edgeIndex1 = rs.lastEdgeIndex; - var edgeIndex2 = i; + this.notify({ + type: 'viewport' + }); + } - var numEdges1 = rs.lastNumEdges; + return this; // chaining + }, - var curveStyle = edge.pstyle('curve-style').value; + center: function center(elements) { + var pan = this.getCenterPan(elements); - var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments'; + if (pan) { + this._private.pan = pan; - // whether the normalised pair order is the reverse of the edge's src-tgt order - var edgeIsSwapped = src.id() !== edge.source().id(); + this.emit('pan viewport'); - var ctrlptDists = edge.pstyle('control-point-distances'); - var loopDir = edge.pstyle('loop-direction').pfValue; - var loopSwp = edge.pstyle('loop-sweep').pfValue; - var ctrlptWs = edge.pstyle('control-point-weights'); - var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1; - var stepSize = edge.pstyle('control-point-step-size').pfValue; - var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined; - var ctrlptWeight = ctrlptWs.value[0]; - var edgeDistances = edge.pstyle('edge-distances').value; - var segmentWs = edge.pstyle('segment-weights'); - var segmentDs = edge.pstyle('segment-distances'); - var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length); - var srcEndpt = edge.pstyle('source-endpoint').value; - var tgtEndpt = edge.pstyle('target-endpoint').value; - var srcArrShape = edge.pstyle('source-arrow-shape').value; - var tgtArrShape = edge.pstyle('target-arrow-shape').value; - var arrowScale = edge.pstyle('arrow-scale').value; - var lineWidth = edge.pstyle('width').pfValue; + this.notify({ // notify the renderer that the viewport changed + type: 'viewport' + }); + } - var srcX1 = rs.lastSrcCtlPtX; - var srcY1 = rs.lastSrcCtlPtY; - var srcW1 = rs.lastSrcCtlPtW; - var srcH1 = rs.lastSrcCtlPtH; + return this; // chaining + }, - var tgtX1 = rs.lastTgtCtlPtX; - var tgtY1 = rs.lastTgtCtlPtY; - var tgtW1 = rs.lastTgtCtlPtW; - var tgtH1 = rs.lastTgtCtlPtH; + getCenterPan: function getCenterPan(elements, zoom) { + if (!this._private.panningEnabled) { + return; + } - var curveStyle1 = rs.lastCurveStyle; - var curveStyle2 = curveStyle; + if (is.string(elements)) { + var selector = elements; + elements = this.mutableElements().filter(selector); + } else if (!is.elementOrCollection(elements)) { + elements = this.mutableElements(); + } - var ctrlptDists1 = rs.lastCtrlptDists; - var ctrlptDists2 = ctrlptDists ? ctrlptDists.strValue : null; + if (elements.length === 0) { + return; + } // can't centre pan to nothing - var ctrlptWs1 = rs.lastCtrlptWs; - var ctrlptWs2 = ctrlptWs.strValue; + var bb = elements.boundingBox(); + var w = this.width(); + var h = this.height(); + zoom = zoom === undefined ? this._private.zoom : zoom; - var segmentWs1 = rs.lastSegmentWs; - var segmentWs2 = segmentWs.strValue; + var pan = { // middle + x: (w - zoom * (bb.x1 + bb.x2)) / 2, + y: (h - zoom * (bb.y1 + bb.y2)) / 2 + }; - var segmentDs1 = rs.lastSegmentDs; - var segmentDs2 = segmentDs.strValue; + return pan; + }, - var stepSize1 = rs.lastStepSize; - var stepSize2 = stepSize; + reset: function reset() { + if (!this._private.panningEnabled || !this._private.zoomingEnabled) { + return this; + } - var loopDir1 = rs.lastLoopDir; - var loopDir2 = loopDir; + this.viewport({ + pan: { x: 0, y: 0 }, + zoom: 1 + }); - var loopSwp1 = rs.lastLoopSwp; - var loopSwp2 = loopSwp; + return this; // chaining + }, - var edgeDistances1 = rs.lastEdgeDistances; - var edgeDistances2 = edgeDistances; + invalidateSize: function invalidateSize() { + this._private.sizeCache = null; + }, - var srcEndpt1 = rs.lastSrcEndpt; - var srcEndpt2 = srcEndpt; + size: function size() { + var _p = this._private; + var container = _p.container; - var tgtEndpt1 = rs.lastTgtEndpt; - var tgtEndpt2 = tgtEndpt; + return _p.sizeCache = _p.sizeCache || (container ? function () { + var style = window.getComputedStyle(container); + var val = function val(name) { + return parseFloat(style.getPropertyValue(name)); + }; - var srcArr1 = rs.lastSrcArr; - var srcArr2 = srcArrShape; + return { + width: container.clientWidth - val('padding-left') - val('padding-right'), + height: container.clientHeight - val('padding-top') - val('padding-bottom') + }; + }() : { // fallback if no container (not 0 b/c can be used for dividing etc) + width: 1, + height: 1 + }); + }, - var tgtArr1 = rs.lastTgtArr; - var tgtArr2 = tgtArrShape; + width: function width() { + return this.size().width; + }, - var lineW1 = rs.lastLineW; - var lineW2 = lineWidth; + height: function height() { + return this.size().height; + }, - var arrScl1 = rs.lastArrScl; - var arrScl2 = arrowScale; + extent: function extent() { + var pan = this._private.pan; + var zoom = this._private.zoom; + var rb = this.renderedExtent(); - if (badBezier) { - rs.badBezier = true; - } else { - rs.badBezier = false; - } + var b = { + x1: (rb.x1 - pan.x) / zoom, + x2: (rb.x2 - pan.x) / zoom, + y1: (rb.y1 - pan.y) / zoom, + y2: (rb.y2 - pan.y) / zoom + }; - var ptCacheHit; + b.w = b.x2 - b.x1; + b.h = b.y2 - b.y1; - if (srcX1 === srcX2 && srcY1 === srcY2 && srcW1 === srcW2 && srcH1 === srcH2 && tgtX1 === tgtX2 && tgtY1 === tgtY2 && tgtW1 === tgtW2 && tgtH1 === tgtH2 && curveStyle1 === curveStyle2 && ctrlptDists1 === ctrlptDists2 && ctrlptWs1 === ctrlptWs2 && segmentWs1 === segmentWs2 && segmentDs1 === segmentDs2 && stepSize1 === stepSize2 && loopDir1 === loopDir2 && loopSwp1 === loopSwp2 && edgeDistances1 === edgeDistances2 && srcEndpt1 === srcEndpt2 && tgtEndpt1 === tgtEndpt2 && srcArr1 === srcArr2 && tgtArr1 === tgtArr2 && lineW1 === lineW2 && arrScl1 === arrScl2 && (edgeIndex1 === edgeIndex2 && numEdges1 === numEdges2 || edgeIsUnbundled)) { - ptCacheHit = true; // then the control points haven't changed and we can skip calculating them - } else { - ptCacheHit = false; + return b; + }, - rs.lastSrcCtlPtX = srcX2; - rs.lastSrcCtlPtY = srcY2; - rs.lastSrcCtlPtW = srcW2; - rs.lastSrcCtlPtH = srcH2; - rs.lastTgtCtlPtX = tgtX2; - rs.lastTgtCtlPtY = tgtY2; - rs.lastTgtCtlPtW = tgtW2; - rs.lastTgtCtlPtH = tgtH2; - rs.lastEdgeIndex = edgeIndex2; - rs.lastNumEdges = numEdges2; - rs.lastCurveStyle = curveStyle2; - rs.lastCtrlptDists = ctrlptDists2; - rs.lastCtrlptWs = ctrlptWs2; - rs.lastSegmentDs = segmentDs2; - rs.lastSegmentWs = segmentWs2; - rs.lastStepSize = stepSize2; - rs.lastLoopDir = loopDir2; - rs.lastLoopSwp = loopSwp2; - rs.lastEdgeDistances = edgeDistances2; - rs.lastSrcEndpt = srcEndpt2; - rs.lastTgtEndpt = tgtEndpt2; - rs.lastSrcArr = srcArr2; - rs.lastTgtArr = tgtArr2; - rs.lastLineW = lineW2; - rs.lastArrScl = arrScl2; - } + renderedExtent: function renderedExtent() { + var width = this.width(); + var height = this.height(); - if (!ptCacheHit) { + return { + x1: 0, + y1: 0, + x2: width, + y2: height, + w: width, + h: height + }; + } +}; - if (!pairEdges.calculatedIntersection && src !== tgt && (pairEdges.hasBezier || pairEdges.hasUnbundled)) { +// aliases +corefn.centre = corefn.center; - pairEdges.calculatedIntersection = true; +// backwards compatibility +corefn.autolockNodes = corefn.autolock; +corefn.autoungrabifyNodes = corefn.autoungrabify; - // pt outside src shape to calc distance/displacement from src to tgt - var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0); +module.exports = corefn; - pairEdges.srcIntn = srcOutside; +/***/ }), +/* 94 */ +/***/ (function(module, exports, __webpack_require__) { - // pt outside tgt shape to calc distance/displacement from src to tgt - var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0); +"use strict"; - pairEdges.tgtIntn = tgtOutside; - var midptSrcPts = { - x1: srcOutside[0], - x2: tgtOutside[0], - y1: srcOutside[1], - y2: tgtOutside[1] - }; +var util = __webpack_require__(1); +var define = __webpack_require__(4); +var Collection = __webpack_require__(7); +var Core = __webpack_require__(12); +var incExts = __webpack_require__(95); +var is = __webpack_require__(0); +var Emitter = __webpack_require__(11); - var posPts = { - x1: srcPos.x, - x2: tgtPos.x, - y1: srcPos.y, - y2: tgtPos.y - }; +// registered extensions to cytoscape, indexed by name +var extensions = {}; - var dy = tgtOutside[1] - srcOutside[1]; - var dx = tgtOutside[0] - srcOutside[0]; - var l = Math.sqrt(dx * dx + dy * dy); +// registered modules for extensions, indexed by name +var modules = {}; - var vector = { - x: dx, - y: dy - }; +function setExtension(type, name, registrant) { - var vectorNorm = { - x: vector.x / l, - y: vector.y / l - }; - vectorNormInverse = { - x: -vectorNorm.y, - y: vectorNorm.x - }; + var ext = registrant; - // if node shapes overlap, then no ctrl pts to draw - if (tgtShape.checkPoint(srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y) && srcShape.checkPoint(tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y)) { - vectorNormInverse = {}; - badBezier = true; - } - } + var overrideErr = function overrideErr(field) { + util.error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden'); + }; - if (!edgeIsSwapped) { - rs.srcIntn = pairEdges.srcIntn; - rs.tgtIntn = pairEdges.tgtIntn; - } else { - // ensure that the per-edge cached value for intersections are correct for swapped bundled edges - rs.srcIntn = pairEdges.tgtIntn; - rs.tgtIntn = pairEdges.srcIntn; - } + if (type === 'core') { + if (Core.prototype[name]) { + return overrideErr(name); + } else { + Core.prototype[name] = registrant; + } + } else if (type === 'collection') { + if (Collection.prototype[name]) { + return overrideErr(name); + } else { + Collection.prototype[name] = registrant; + } + } else if (type === 'layout') { + // fill in missing layout functions in the prototype - if (src === tgt) { - // Self-edge + var Layout = function Layout(options) { + this.options = options; - rs.edgeType = 'self'; + registrant.call(this, options); - var j = i; - var loopDist = stepSize; + // make sure layout has _private for use w/ std apis like .on() + if (!is.plainObject(this._private)) { + this._private = {}; + } - if (edgeIsUnbundled) { - j = 0; - loopDist = ctrlptDist; - } + this._private.cy = options.cy; + this._private.listeners = []; - var loopAngle = loopDir - Math.PI / 2; - var outAngle = loopAngle - loopSwp / 2; - var inAngle = loopAngle + loopSwp / 2; + this.createEmitter(); + }; - // increase by step size for overlapping loops, keyed on direction and sweep values - var dc = String(loopDir + '_' + loopSwp); - j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc]; + var layoutProto = Layout.prototype = Object.create(registrant.prototype); - rs.ctrlpts = [srcPos.x + Math.cos(outAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.y + Math.sin(outAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.x + Math.cos(inAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.y + Math.sin(inAngle) * 1.4 * loopDist * (j / 3 + 1)]; - } else if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src))) { - // Compound edge + var optLayoutFns = []; - rs.edgeType = 'compound'; + for (var i = 0; i < optLayoutFns.length; i++) { + var fnName = optLayoutFns[i]; - // because the line approximation doesn't apply for compound beziers - // (loop/self edges are already elided b/c of cheap src==tgt check) - rs.badBezier = false; + layoutProto[fnName] = layoutProto[fnName] || function () { + return this; + }; + } - var j = i; - var loopDist = stepSize; + // either .start() or .run() is defined, so autogen the other + if (layoutProto.start && !layoutProto.run) { + layoutProto.run = function () { + this.start();return this; + }; + } else if (!layoutProto.start && layoutProto.run) { + layoutProto.start = function () { + this.run();return this; + }; + } - if (edgeIsUnbundled) { - j = 0; - loopDist = ctrlptDist; + var regStop = registrant.prototype.stop; + layoutProto.stop = function () { + var opts = this.options; + + if (opts && opts.animate) { + var anis = this.animations; + + if (anis) { + for (var _i = 0; _i < anis.length; _i++) { + anis[_i].stop(); } + } + } - var loopW = 50; + if (regStop) { + regStop.call(this); + } else { + this.emit('layoutstop'); + } - var loopaPos = { - x: srcPos.x - srcW / 2, - y: srcPos.y - srcH / 2 - }; + return this; + }; - var loopbPos = { - x: tgtPos.x - tgtW / 2, - y: tgtPos.y - tgtH / 2 - }; + if (!layoutProto.destroy) { + layoutProto.destroy = function () { + return this; + }; + } - var loopPos = { - x: Math.min(loopaPos.x, loopbPos.x), - y: Math.min(loopaPos.y, loopbPos.y) - }; + layoutProto.cy = function () { + return this._private.cy; + }; - // avoids cases with impossible beziers - var minCompoundStretch = 0.5; - var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01)); - var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01)); + var getCy = function getCy(layout) { + return layout._private.cy; + }; - rs.ctrlpts = [loopPos.x, loopPos.y - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchA, loopPos.x - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchB, loopPos.y]; - } else if (curveStyle === 'segments') { - // Segments (multiple straight lines) + util.assign(layoutProto, { + createEmitter: function createEmitter() { + this._private.emitter = new Emitter({ + eventFields: function eventFields(layout) { + return { + layout: layout, + cy: getCy(layout), + target: layout + }; + }, + bubble: function bubble() { + return true; + }, + parent: function parent(layout) { + return getCy(layout); + }, + context: this + }); - rs.edgeType = 'segments'; - rs.segpts = []; + return this; + }, + emitter: function emitter() { + return this._private.emitter; + }, + on: function on(evt, cb) { + this.emitter().on(evt, cb);return this; + }, + one: function one(evt, cb) { + this.emitter().one(evt, cb);return this; + }, + once: function once(evt, cb) { + this.emitter().one(evt, cb);return this; + }, + removeListener: function removeListener(evt, cb) { + this.emitter().removeListener(evt, cb);return this; + }, + emit: function emit(evt, params) { + this.emitter().emit(evt, params);return this; + } + }); - for (var s = 0; s < segmentsN; s++) { - var w = segmentWs.pfValue[s]; - var d = segmentDs.pfValue[s]; + define.eventAliasesOn(layoutProto); - var w1 = 1 - w; - var w2 = w; + ext = Layout; // replace with our wrapped layout + } else if (type === 'renderer' && name !== 'null' && name !== 'base') { + // user registered renderers inherit from base - var midptPts = edgeDistances === 'node-position' ? posPts : midptSrcPts; + var BaseRenderer = getExtension('renderer', 'base'); + var bProto = BaseRenderer.prototype; + var RegistrantRenderer = registrant; + var rProto = registrant.prototype; - var adjustedMidpt = { - x: midptPts.x1 * w1 + midptPts.x2 * w2, - y: midptPts.y1 * w1 + midptPts.y2 * w2 - }; + var Renderer = function Renderer() { + BaseRenderer.apply(this, arguments); + RegistrantRenderer.apply(this, arguments); + }; - rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d); - } + var proto = Renderer.prototype; - // Straight edge - } else if (pairEdges.length % 2 === 1 && i === Math.floor(pairEdges.length / 2) && !edgeIsUnbundled) { + for (var pName in bProto) { + var pVal = bProto[pName]; + var existsInR = rProto[pName] != null; - rs.edgeType = 'straight'; - } else { - // (Multi)bezier + if (existsInR) { + return overrideErr(pName); + } - var multi = edgeIsUnbundled; + proto[pName] = pVal; // take impl from base + } - rs.edgeType = multi ? 'multibezier' : 'bezier'; - rs.ctrlpts = []; + for (var _pName in rProto) { + proto[_pName] = rProto[_pName]; // take impl from registrant + } - for (var b = 0; b < bezierN; b++) { - var normctrlptDist = (0.5 - pairEdges.length / 2 + i) * stepSize; - var manctrlptDist; - var sign = math.signum(normctrlptDist); + bProto.clientFunctions.forEach(function (name) { + proto[name] = proto[name] || function () { + util.error('Renderer does not implement `renderer.' + name + '()` on its prototype'); + }; + }); - if (multi) { - ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size - ctrlptWeight = ctrlptWs.value[b]; - } + ext = Renderer; + } - if (edgeIsUnbundled) { - // multi or single unbundled - manctrlptDist = ctrlptDist; - } else { - manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined; - } + return util.setMap({ + map: extensions, + keys: [type, name], + value: ext + }); +} - var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist; +function getExtension(type, name) { + return util.getMap({ + map: extensions, + keys: [type, name] + }); +} - var w1 = 1 - ctrlptWeight; - var w2 = ctrlptWeight; +function setModule(type, name, moduleType, moduleName, registrant) { + return util.setMap({ + map: modules, + keys: [type, name, moduleType, moduleName], + value: registrant + }); +} - if (edgeIsSwapped) { - var temp = w1; - w1 = w2; - w2 = temp; - } +function getModule(type, name, moduleType, moduleName) { + return util.getMap({ + map: modules, + keys: [type, name, moduleType, moduleName] + }); +} - var midptPts = edgeDistances === 'node-position' ? posPts : midptSrcPts; +var extension = function extension() { + // e.g. extension('renderer', 'svg') + if (arguments.length === 2) { + return getExtension.apply(null, arguments); + } - var adjustedMidpt = { - x: midptPts.x1 * w1 + midptPts.x2 * w2, - y: midptPts.y1 * w1 + midptPts.y2 * w2 - }; + // e.g. extension('renderer', 'svg', { ... }) + else if (arguments.length === 3) { + return setExtension.apply(null, arguments); + } - rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint); - } - } + // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse') + else if (arguments.length === 4) { + return getModule.apply(null, arguments); + } - // find endpts for edge - this.findEndpoints(edge); + // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... }) + else if (arguments.length === 5) { + return setModule.apply(null, arguments); + } else { + util.error('Invalid extension access syntax'); + } +}; - var badStart = !is.number(rs.startX) || !is.number(rs.startY); - var badAStart = !is.number(rs.arrowStartX) || !is.number(rs.arrowStartY); - var badEnd = !is.number(rs.endX) || !is.number(rs.endY); - var badAEnd = !is.number(rs.arrowEndX) || !is.number(rs.arrowEndY); +// allows a core instance to access extensions internally +Core.prototype.extension = extension; - var minCpADistFactor = 3; - var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth; - var minCpADist = minCpADistFactor * arrowW; +// included extensions +incExts.forEach(function (group) { + group.extensions.forEach(function (ext) { + setExtension(group.type, ext.name, ext.impl); + }); +}); - if (rs.edgeType === 'bezier') { - var startACpDist = math.dist({ x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.startX, y: rs.startY }); - var closeStartACp = startACpDist < minCpADist; - var endACpDist = math.dist({ x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.endX, y: rs.endY }); - var closeEndACp = endACpDist < minCpADist; +module.exports = extension; - var overlapping = false; +/***/ }), +/* 95 */ +/***/ (function(module, exports, __webpack_require__) { - if (badStart || badAStart || closeStartACp) { - overlapping = true; +"use strict"; - // project control point along line from src centre to outside the src shape - // (otherwise intersection will yield nothing) - var cpD = { // delta - x: rs.ctrlpts[0] - srcPos.x, - y: rs.ctrlpts[1] - srcPos.y - }; - var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line - var cpM = { // normalised delta - x: cpD.x / cpL, - y: cpD.y / cpL - }; - var radius = Math.max(srcW, srcH); - var cpProj = { // *2 radius guarantees outside shape - x: rs.ctrlpts[0] + cpM.x * 2 * radius, - y: rs.ctrlpts[1] + cpM.y * 2 * radius - }; - var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0); +module.exports = [{ + type: 'layout', + extensions: __webpack_require__(96) +}, { + type: 'renderer', + extensions: __webpack_require__(105) +}]; - if (closeStartACp) { - rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist); - rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist); - } else { - rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist; - rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist; - } - } +/***/ }), +/* 96 */ +/***/ (function(module, exports, __webpack_require__) { - if (badEnd || badAEnd || closeEndACp) { - overlapping = true; +"use strict"; - // project control point along line from tgt centre to outside the tgt shape - // (otherwise intersection will yield nothing) - var cpD = { // delta - x: rs.ctrlpts[0] - tgtPos.x, - y: rs.ctrlpts[1] - tgtPos.y - }; - var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line - var cpM = { // normalised delta - x: cpD.x / cpL, - y: cpD.y / cpL - }; - var radius = Math.max(srcW, srcH); - var cpProj = { // *2 radius guarantees outside shape - x: rs.ctrlpts[0] + cpM.x * 2 * radius, - y: rs.ctrlpts[1] + cpM.y * 2 * radius - }; - var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, cpProj.x, cpProj.y, 0); +module.exports = [{ name: 'breadthfirst', impl: __webpack_require__(97) }, { name: 'circle', impl: __webpack_require__(98) }, { name: 'concentric', impl: __webpack_require__(99) }, { name: 'cose', impl: __webpack_require__(100) }, { name: 'grid', impl: __webpack_require__(101) }, { name: 'null', impl: __webpack_require__(102) }, { name: 'preset', impl: __webpack_require__(103) }, { name: 'random', impl: __webpack_require__(104) }]; - if (closeEndACp) { - rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - endACpDist); - rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - endACpDist); - } else { - rs.ctrlpts[0] = tgtCtrlPtIntn[0] + cpM.x * minCpADist; - rs.ctrlpts[1] = tgtCtrlPtIntn[1] + cpM.y * minCpADist; - } - } +/***/ }), +/* 97 */ +/***/ (function(module, exports, __webpack_require__) { - if (overlapping) { - // recalc endpts - this.findEndpoints(edge); - } - } +"use strict"; - if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') { - rs.allpts = []; - rs.allpts.push(rs.startX, rs.startY); +var util = __webpack_require__(1); +var math = __webpack_require__(2); +var is = __webpack_require__(0); - for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) { - // ctrl pt itself - rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); +var defaults = { + fit: true, // whether to fit the viewport to the graph + directed: false, // whether the tree is directed downwards (or edges can point in any direction if false) + padding: 30, // padding on fit + circle: false, // put depths in concentric circles if true, put depths top down if false + spacingFactor: 1.75, // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap) + boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } + avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space + nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm + roots: undefined, // the roots of the trees + maximalAdjustments: 0, // how many times to try to position the nodes in a maximal way (i.e. no backtracking) + animate: false, // whether to transition the node positions + animationDuration: 500, // duration of animation in ms if enabled + animationEasing: undefined, // easing of animation if enabled, + animateFilter: function animateFilter(node, i) { + return true; + }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts + ready: undefined, // callback on layoutready + stop: undefined, // callback on layoutstop + transform: function transform(node, position) { + return position; + } // transform a given node position. Useful for changing flow direction in discrete layouts +}; - // the midpt between ctrlpts as intermediate destination pts - if (b + 3 < rs.ctrlpts.length) { - rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2); - } - } +function BreadthFirstLayout(options) { + this.options = util.extend({}, defaults, options); +} - rs.allpts.push(rs.endX, rs.endY); +BreadthFirstLayout.prototype.run = function () { + var params = this.options; + var options = params; - var m, mt; - if (rs.ctrlpts.length / 2 % 2 === 0) { - m = rs.allpts.length / 2 - 1; + var cy = params.cy; + var eles = options.eles; + var nodes = eles.nodes().not(':parent'); + var graph = eles; - rs.midX = rs.allpts[m]; - rs.midY = rs.allpts[m + 1]; - } else { - m = rs.allpts.length / 2 - 3; - mt = 0.5; + var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { + x1: 0, y1: 0, w: cy.width(), h: cy.height() + }); - rs.midX = math.qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt); - rs.midY = math.qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt); - } - } else if (rs.edgeType === 'straight') { - // need to calc these after endpts - rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; + var roots = void 0; + if (is.elementOrCollection(options.roots)) { + roots = options.roots; + } else if (is.array(options.roots)) { + var rootsArray = []; - // default midpt for labels etc - rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4; - rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4; - } else if (rs.edgeType === 'segments') { - rs.allpts = []; - rs.allpts.push(rs.startX, rs.startY); - rs.allpts.push.apply(rs.allpts, rs.segpts); - rs.allpts.push(rs.endX, rs.endY); + for (var i = 0; i < options.roots.length; i++) { + var id = options.roots[i]; + var ele = cy.getElementById(id); + rootsArray.push(ele); + } - if (rs.segpts.length % 4 === 0) { - var i2 = rs.segpts.length / 2; - var i1 = i2 - 2; + roots = cy.collection(rootsArray); + } else if (is.string(options.roots)) { + roots = cy.$(options.roots); + } else { + if (options.directed) { + roots = nodes.roots(); + } else { + var components = []; + var unhandledNodes = nodes; - rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2; - rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2; - } else { - var i1 = rs.segpts.length / 2 - 1; + var _loop = function _loop() { + var currComp = cy.collection(); - rs.midX = rs.segpts[i1]; - rs.midY = rs.segpts[i1 + 1]; - } - } + eles.bfs({ + roots: unhandledNodes[0], + visit: function visit(node, edge, pNode, i, depth) { + currComp = currComp.add(node); + }, + directed: false + }); - this.storeEdgeProjections(edge); - this.calculateArrowAngles(edge); - } // if point cache miss + unhandledNodes = unhandledNodes.not(currComp); + components.push(currComp); + }; - this.recalculateEdgeLabelProjections(edge); - this.calculateLabelAngles(edge); - } // for pair edges - } // for pair ids + while (unhandledNodes.length > 0) { + _loop(); + } - for (var i = 0; i < haystackEdges.length; i++) { - var edge = haystackEdges[i]; - var _p = edge._private; - var rscratch = _p.rscratch; - var rs = rscratch; + roots = cy.collection(); - if (!rscratch.haystack) { - var angle = Math.random() * 2 * Math.PI; + var _loop2 = function _loop2(_i) { + var comp = components[_i]; + var maxDegree = comp.maxDegree(false); + var compRoots = comp.filter(function (ele) { + return ele.degree(false) === maxDegree; + }); - rscratch.source = { - x: Math.cos(angle), - y: Math.sin(angle) + roots = roots.add(compRoots); }; - var angle = Math.random() * 2 * Math.PI; - - rscratch.target = { - x: Math.cos(angle), - y: Math.sin(angle) - }; + for (var _i = 0; _i < components.length; _i++) { + _loop2(_i); + } } + } - var src = _p.source; - var tgt = _p.target; - var srcPos = src.position(); - var tgtPos = tgt.position(); - var srcW = src.width(); - var tgtW = tgt.width(); - var srcH = src.height(); - var tgtH = tgt.height(); - var radius = edge.pstyle('haystack-radius').value; - var halfRadius = radius / 2; // b/c have to half width/height + var depths = []; + var foundByBfs = {}; + var id2depth = {}; + var prevNode = {}; + var prevEdge = {}; + var successors = {}; - rs.haystackPts = rs.allpts = [rs.source.x * srcW * halfRadius + srcPos.x, rs.source.y * srcH * halfRadius + srcPos.y, rs.target.x * tgtW * halfRadius + tgtPos.x, rs.target.y * tgtH * halfRadius + tgtPos.y]; + // find the depths of the nodes + graph.bfs({ + roots: roots, + directed: options.directed, + visit: function visit(node, edge, pNode, i, depth) { + var ele = node[0]; + var id = ele.id(); - rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2; - rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; + if (!depths[depth]) { + depths[depth] = []; + } - // always override as haystack in case set to different type previously - rscratch.edgeType = rscratch.lastCurveStyle = 'haystack'; - rscratch.haystack = true; + depths[depth].push(ele); + foundByBfs[id] = true; + id2depth[id] = depth; + prevNode[id] = pNode; + prevEdge[id] = edge; - this.storeEdgeProjections(edge); - this.calculateArrowAngles(edge); - this.recalculateEdgeLabelProjections(edge); - this.calculateLabelAngles(edge); - } -}; + if (pNode) { + var prevId = pNode.id(); + var succ = successors[prevId] = successors[prevId] || []; -function getPts(pts) { - var retPts = []; + succ.push(node); + } + } + }); - if (pts == null) { - return; + // check for nodes not found by bfs + var orphanNodes = []; + for (var _i2 = 0; _i2 < nodes.length; _i2++) { + var _ele = nodes[_i2]; + + if (foundByBfs[_ele.id()]) { + continue; + } else { + orphanNodes.push(_ele); + } } - for (var i = 0; i < pts.length; i += 2) { - var x = pts[i]; - var y = pts[i + 1]; + // assign orphan nodes a depth from their neighborhood + var maxChecks = orphanNodes.length * 3; + var checks = 0; + while (orphanNodes.length !== 0 && checks < maxChecks) { + var node = orphanNodes.shift(); + var neighbors = node.neighborhood().nodes(); + var assignedDepth = false; - retPts.push({ x: x, y: y }); - } + for (var _i3 = 0; _i3 < neighbors.length; _i3++) { + var depth = id2depth[neighbors[_i3].id()]; - return retPts; -} + if (depth !== undefined) { + depths[depth].push(node); + assignedDepth = true; + break; + } + } -BRp.getSegmentPoints = function (edge) { - var rs = edge[0]._private.rscratch; - var type = rs.edgeType; + if (!assignedDepth) { + orphanNodes.push(node); + } - if (type === 'segments') { - return getPts(rs.segpts); + checks++; } -}; -BRp.getControlPoints = function (edge) { - var rs = edge[0]._private.rscratch; - var type = rs.edgeType; + // assign orphan nodes that are still left to the depth of their subgraph + while (orphanNodes.length !== 0) { + var _node = orphanNodes.shift(); + //let subgraph = graph.bfs( node ).path; + var _assignedDepth = false; - if (type === 'bezier' || type === 'multibezier') { - return getPts(rs.ctrlpts); - } -}; + // for( let i = 0; i < subgraph.length; i++ ){ + // let depth = id2depth[ subgraph[i].id() ]; -BRp.getEdgeMidpoint = function (edge) { - var rs = edge[0]._private.rscratch; + // if( depth !== undefined ){ + // depths[depth].push( node ); + // assignedDepth = true; + // break; + // } + // } - return { - x: rs.midX, - y: rs.midY - }; -}; + if (!_assignedDepth) { + // worst case if the graph really isn't tree friendly, then just dump it in 0 + if (depths.length === 0) { + depths.push([]); + } -module.exports = BRp; + depths[0].push(_node); + } + } -/***/ }), -/* 89 */ -/***/ (function(module, exports, __webpack_require__) { + // assign the nodes a depth and index + var assignDepthsToEles = function assignDepthsToEles() { + for (var _i4 = 0; _i4 < depths.length; _i4++) { + var _eles = depths[_i4]; + + for (var j = 0; j < _eles.length; j++) { + var _ele2 = _eles[j]; -"use strict"; + if (_ele2 == null) { + _eles.splice(j, 1); + j--; + continue; + } + _ele2._private.scratch.breadthfirst = { + depth: _i4, + index: j + }; + } + } + }; + assignDepthsToEles(); -var math = __webpack_require__(2); -var is = __webpack_require__(0); + var intersectsDepth = function intersectsDepth(node) { + // returns true if has edges pointing in from a higher depth + var edges = node.connectedEdges(function (ele) { + return ele.data('target') === node.id(); + }); + var thisInfo = node._private.scratch.breadthfirst; + var highestDepthOfOther = 0; + var highestOther = void 0; + for (var _i5 = 0; _i5 < edges.length; _i5++) { + var edge = edges[_i5]; + var otherNode = edge.source()[0]; + var otherInfo = otherNode._private.scratch.breadthfirst; -var BRp = {}; + if (thisInfo.depth <= otherInfo.depth && highestDepthOfOther < otherInfo.depth) { + highestDepthOfOther = otherInfo.depth; + highestOther = otherNode; + } + } -BRp.manualEndptToPx = function (node, prop) { - var r = this; - var npos = node.position(); - var w = node.outerWidth(); - var h = node.outerHeight(); + return highestOther; + }; - if (prop.value.length === 2) { - var p = [prop.pfValue[0], prop.pfValue[1]]; + // make maximal if so set by adjusting depths + for (var adj = 0; adj < options.maximalAdjustments; adj++) { - if (prop.units[0] === '%') { - p[0] = p[0] * w; - } + var nDepths = depths.length; + var elesToMove = []; + for (var _i6 = 0; _i6 < nDepths; _i6++) { + var _depth = depths[_i6]; - if (prop.units[1] === '%') { - p[1] = p[1] * h; - } + var nDepth = _depth.length; + for (var j = 0; j < nDepth; j++) { + var _ele3 = _depth[j]; + var info = _ele3._private.scratch.breadthfirst; + var intEle = intersectsDepth(_ele3); - p[0] += npos.x; - p[1] += npos.y; + if (intEle) { + info.intEle = intEle; + elesToMove.push(_ele3); + } + } + } - return p; - } else { - var angle = prop.pfValue[0]; + for (var _i7 = 0; _i7 < elesToMove.length; _i7++) { + var _ele4 = elesToMove[_i7]; + var _info = _ele4._private.scratch.breadthfirst; + var _intEle = _info.intEle; + var intInfo = _intEle._private.scratch.breadthfirst; - angle = -Math.PI / 2 + angle; // start at 12 o'clock + depths[_info.depth][_info.index] = null; // remove from old depth & index (create hole to be cleaned) - var l = 2 * Math.max(w, h); + // add to end of new depth + var newDepth = intInfo.depth + 1; + while (newDepth > depths.length - 1) { + depths.push([]); + } + depths[newDepth].push(_ele4); - var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l]; + _info.depth = newDepth; + _info.index = depths[newDepth].length - 1; + } - return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0); + assignDepthsToEles(); } -}; -BRp.findEndpoints = function (edge) { - var r = this; - var intersect = void 0; + // find min distance we need to leave between nodes + var minDistance = 0; + if (options.avoidOverlap) { + for (var _i8 = 0; _i8 < nodes.length; _i8++) { + var n = nodes[_i8]; + var nbb = n.layoutDimensions(options); + var w = nbb.w; + var h = nbb.h; - var source = edge.source()[0]; - var target = edge.target()[0]; + minDistance = Math.max(minDistance, w, h); + } + } - var srcPos = source.position(); - var tgtPos = target.position(); + // get the weighted percent for an element based on its connectivity to other levels + var cachedWeightedPercent = {}; + var getWeightedPercent = function getWeightedPercent(ele) { + if (cachedWeightedPercent[ele.id()]) { + return cachedWeightedPercent[ele.id()]; + } - var tgtArShape = edge.pstyle('target-arrow-shape').value; - var srcArShape = edge.pstyle('source-arrow-shape').value; + var eleDepth = ele._private.scratch.breadthfirst.depth; + var neighbors = ele.neighborhood().nodes().not(':parent').intersection(nodes); + var percent = 0; + var samples = 0; - var tgtDist = edge.pstyle('target-distance-from-node').pfValue; - var srcDist = edge.pstyle('source-distance-from-node').pfValue; + for (var _i9 = 0; _i9 < neighbors.length; _i9++) { + var neighbor = neighbors[_i9]; + var bf = neighbor._private.scratch.breadthfirst; + var index = bf.index; + var _depth2 = bf.depth; + var _nDepth = depths[_depth2].length; - var rs = edge._private.rscratch; + if (eleDepth > _depth2 || eleDepth === 0) { + // only get influenced by elements above + percent += index / _nDepth; + samples++; + } + } - var et = rs.edgeType; - var self = et === 'self' || et === 'compound'; - var bezier = et === 'bezier' || et === 'multibezier' || self; - var multi = et !== 'bezier'; - var lines = et === 'straight' || et === 'segments'; - var segments = et === 'segments'; - var hasEndpts = bezier || multi || lines; - var srcManEndpt = edge.pstyle('source-endpoint'); - var srcManEndptVal = self ? 'outside-to-node' : srcManEndpt.value; - var tgtManEndpt = edge.pstyle('target-endpoint'); - var tgtManEndptVal = self ? 'outside-to-node' : tgtManEndpt.value; + samples = Math.max(1, samples); + percent = percent / samples; - rs.srcManEndpt = srcManEndpt; - rs.tgtManEndpt = tgtManEndpt; + if (samples === 0) { + // so lone nodes have a "don't care" state in sorting + percent = undefined; + } - var p1 = void 0; // last known point of edge on target side - var p2 = void 0; // last known point of edge on source side + cachedWeightedPercent[ele.id()] = percent; + return percent; + }; - var p1_i = void 0; // point to intersect with target shape - var p2_i = void 0; // point to intersect with source shape + // rearrange the indices in each depth level based on connectivity - if (bezier) { - var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]]; - var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart; + var sortFn = function sortFn(a, b) { + var apct = getWeightedPercent(a); + var bpct = getWeightedPercent(b); - p1 = cpEnd; - p2 = cpStart; - } else if (lines) { - var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2); - var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2); + return apct - bpct; + }; - p1 = tgtArrowFromPt; - p2 = srcArrowFromPt; - } + for (var times = 0; times < 3; times++) { + // do it a few times b/c the depths are dynamic and we want a more stable result - if (tgtManEndptVal === 'inside-to-node') { - intersect = [tgtPos.x, tgtPos.y]; - } else if (tgtManEndpt.units) { - intersect = this.manualEndptToPx(target, tgtManEndpt); - } else if (tgtManEndptVal === 'outside-to-line') { - intersect = rs.tgtIntn; // use cached value from ctrlpt calc - } else { - if (tgtManEndptVal === 'outside-to-node') { - p1_i = p1; - } else if (tgtManEndptVal === 'outside-to-line') { - p1_i = [srcPos.x, srcPos.y]; + for (var _i10 = 0; _i10 < depths.length; _i10++) { + depths[_i10] = depths[_i10].sort(sortFn); } + assignDepthsToEles(); // and update + } - intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0); + var biggestDepthSize = 0; + for (var _i11 = 0; _i11 < depths.length; _i11++) { + biggestDepthSize = Math.max(depths[_i11].length, biggestDepthSize); } - var arrowEnd = math.shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist); - var edgeEnd = math.shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist); + var center = { + x: bb.x1 + bb.w / 2, + y: bb.x1 + bb.h / 2 + }; - rs.endX = edgeEnd[0]; - rs.endY = edgeEnd[1]; + var getPosition = function getPosition(ele, isBottomDepth) { + var info = ele._private.scratch.breadthfirst; + var depth = info.depth; + var index = info.index; + var depthSize = depths[depth].length; - rs.arrowEndX = arrowEnd[0]; - rs.arrowEndY = arrowEnd[1]; + var distanceX = Math.max(bb.w / (depthSize + 1), minDistance); + var distanceY = Math.max(bb.h / (depths.length + 1), minDistance); + var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length); + radiusStepSize = Math.max(radiusStepSize, minDistance); - if (srcManEndptVal === 'inside-to-node') { - intersect = [srcPos.x, srcPos.y]; - } else if (srcManEndpt.units) { - intersect = this.manualEndptToPx(source, srcManEndpt); - } else if (srcManEndptVal === 'outside-to-line') { - intersect = rs.srcIntn; // use cached value from ctrlpt calc - } else { - if (srcManEndptVal === 'outside-to-node') { - p2_i = p2; - } else if (srcManEndptVal === 'outside-to-line') { - p2_i = [tgtPos.x, tgtPos.y]; - } + if (!options.circle) { - intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0); - } + var epos = { + x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX, + y: (depth + 1) * distanceY + }; - var arrowStart = math.shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist); - var edgeStart = math.shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist); + if (isBottomDepth) { + return epos; + } + + // let succs = successors[ ele.id() ]; + // if( succs ){ + // epos.x = 0; + // + // for( let i = 0 ; i < succs.length; i++ ){ + // let spos = pos[ succs[i].id() ]; + // + // epos.x += spos.x; + // } + // + // epos.x /= succs.length; + // } else { + // //debugger; + // } - rs.startX = edgeStart[0]; - rs.startY = edgeStart[1]; + return epos; + } else { + if (options.circle) { + var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0); + var theta = 2 * Math.PI / depths[depth].length * index; - rs.arrowStartX = arrowStart[0]; - rs.arrowStartY = arrowStart[1]; + if (depth === 0 && depths[0].length === 1) { + radius = 1; + } - if (hasEndpts) { - if (!is.number(rs.startX) || !is.number(rs.startY) || !is.number(rs.endX) || !is.number(rs.endY)) { - rs.badLine = true; - } else { - rs.badLine = false; + return { + x: center.x + radius * Math.cos(theta), + y: center.y + radius * Math.sin(theta) + }; + } else { + return { + x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX, + y: (depth + 1) * distanceY + }; + } } - } -}; + }; -BRp.getSourceEndpoint = function (edge) { - var rs = edge[0]._private.rscratch; + // get positions in reverse depth order + var pos = {}; + for (var _i12 = depths.length - 1; _i12 >= 0; _i12--) { + var _depth3 = depths[_i12]; - switch (rs.edgeType) { - case 'haystack': - return { - x: rs.haystackPts[0], - y: rs.haystackPts[1] - }; - default: - return { - x: rs.arrowStartX, - y: rs.arrowStartY - }; + for (var _j = 0; _j < _depth3.length; _j++) { + var _node2 = _depth3[_j]; + + pos[_node2.id()] = getPosition(_node2, _i12 === depths.length - 1); + } } -}; -BRp.getTargetEndpoint = function (edge) { - var rs = edge[0]._private.rscratch; + nodes.layoutPositions(this, options, function (node) { + return pos[node.id()]; + }); - switch (rs.edgeType) { - case 'haystack': - return { - x: rs.haystackPts[2], - y: rs.haystackPts[3] - }; - default: - return { - x: rs.arrowEndX, - y: rs.arrowEndY - }; - } + return this; // chaining }; -module.exports = BRp; +module.exports = BreadthFirstLayout; /***/ }), -/* 90 */ +/* 98 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +var util = __webpack_require__(1); var math = __webpack_require__(2); +var is = __webpack_require__(0); -var BRp = {}; +var defaults = { + fit: true, // whether to fit the viewport to the graph + padding: 30, // the padding on fit + boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } + avoidOverlap: true, // prevents node overlap, may overflow boundingBox and radius if not enough space + nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm + spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up + radius: undefined, // the radius of the circle + startAngle: 3 / 2 * Math.PI, // where nodes start in radians + sweep: undefined, // how many radians should be between the first and last node (defaults to full circle) + clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false) + sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') } + animate: false, // whether to transition the node positions + animationDuration: 500, // duration of animation in ms if enabled + animationEasing: undefined, // easing of animation if enabled + animateFilter: function animateFilter(node, i) { + return true; + }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts + ready: undefined, // callback on layoutready + stop: undefined, // callback on layoutstop + transform: function transform(node, position) { + return position; + } // transform a given node position. Useful for changing flow direction in discrete layouts -function pushBezierPts(r, edge, pts) { - var qbezierAt = function qbezierAt(p1, p2, p3, t) { - return math.qbezierAt(p1, p2, p3, t); - }; - var _p = edge._private; - var bpts = _p.rstyle.bezierPts; +}; - for (var i = 0; i < r.bezierProjPcts.length; i++) { - var p = r.bezierProjPcts[i]; +function CircleLayout(options) { + this.options = util.extend({}, defaults, options); +} - bpts.push({ - x: qbezierAt(pts[0], pts[2], pts[4], p), - y: qbezierAt(pts[1], pts[3], pts[5], p) - }); +CircleLayout.prototype.run = function () { + var params = this.options; + var options = params; + + var cy = params.cy; + var eles = options.eles; + + var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise; + + var nodes = eles.nodes().not(':parent'); + + if (options.sort) { + nodes = nodes.sort(options.sort); } -} -BRp.storeEdgeProjections = function (edge) { - var _p = edge._private; - var rs = _p.rscratch; - var et = rs.edgeType; + var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { + x1: 0, y1: 0, w: cy.width(), h: cy.height() + }); - // clear the cached points state - _p.rstyle.bezierPts = null; - _p.rstyle.linePts = null; - _p.rstyle.haystackPts = null; + var center = { + x: bb.x1 + bb.w / 2, + y: bb.y1 + bb.h / 2 + }; - if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') { - var bpts = _p.rstyle.bezierPts = []; // jshint ignore:line + var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep; + var dTheta = sweep / Math.max(1, nodes.length - 1); + var r = void 0; - for (var i = 0; i + 5 < rs.allpts.length; i += 4) { - pushBezierPts(this, edge, rs.allpts.slice(i, i + 6)); - } - } else if (et === 'segments') { - var lpts = _p.rstyle.linePts = []; + var minDistance = 0; + for (var i = 0; i < nodes.length; i++) { + var n = nodes[i]; + var nbb = n.layoutDimensions(options); + var w = nbb.w; + var h = nbb.h; - for (var i = 0; i + 1 < rs.allpts.length; i += 2) { - lpts.push({ - x: rs.allpts[i], - y: rs.allpts[i + 1] - }); - } - } else if (et === 'haystack') { - var hpts = rs.haystackPts; + minDistance = Math.max(minDistance, w, h); + } - _p.rstyle.haystackPts = [{ x: hpts[0], y: hpts[1] }, { x: hpts[2], y: hpts[3] }]; + if (is.number(options.radius)) { + r = options.radius; + } else if (nodes.length <= 1) { + r = 0; + } else { + r = Math.min(bb.h, bb.w) / 2 - minDistance; } - _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth; -}; + // calculate the radius + if (nodes.length > 1 && options.avoidOverlap) { + // but only if more than one node (can't overlap) + minDistance *= 1.75; // just to have some nice spacing -BRp.recalculateEdgeProjections = function (edges) { - this.findEdgeControlPoints(edges); + var dcos = Math.cos(dTheta) - Math.cos(0); + var dsin = Math.sin(dTheta) - Math.sin(0); + var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping + r = Math.max(rMin, r); + } + + var getPos = function getPos(ele, i) { + var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1); + + var rx = r * Math.cos(theta); + var ry = r * Math.sin(theta); + var pos = { + x: center.x + rx, + y: center.y + ry + }; + + return pos; + }; + + nodes.layoutPositions(this, options, getPos); + + return this; // chaining }; -module.exports = BRp; +module.exports = CircleLayout; /***/ }), -/* 91 */ +/* 99 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(1); +var math = __webpack_require__(2); -var BRp = {}; - -[__webpack_require__(86), __webpack_require__(87), __webpack_require__(88), __webpack_require__(89), __webpack_require__(90), __webpack_require__(92), __webpack_require__(93), __webpack_require__(94), __webpack_require__(95)].forEach(function (props) { - util.extend(BRp, props); -}); - -module.exports = BRp; +var defaults = { + fit: true, // whether to fit the viewport to the graph + padding: 30, // the padding on fit + startAngle: 3 / 2 * Math.PI, // where nodes start in radians + sweep: undefined, // how many radians should be between the first and last node (defaults to full circle) + clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false) + equidistant: false, // whether levels have an equal radial distance betwen them, may cause bounding box overflow + minNodeSpacing: 10, // min spacing between outside of nodes (used for radius adjustment) + boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } + avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space + nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm + height: undefined, // height of layout area (overrides container height) + width: undefined, // width of layout area (overrides container width) + spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up + concentric: function concentric(node) { + // returns numeric value for each node, placing higher nodes in levels towards the centre + return node.degree(); + }, + levelWidth: function levelWidth(nodes) { + // the letiation of concentric values in each level + return nodes.maxDegree() / 4; + }, + animate: false, // whether to transition the node positions + animationDuration: 500, // duration of animation in ms if enabled + animationEasing: undefined, // easing of animation if enabled + animateFilter: function animateFilter(node, i) { + return true; + }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts + ready: undefined, // callback on layoutready + stop: undefined, // callback on layoutstop + transform: function transform(node, position) { + return position; + } // transform a given node position. Useful for changing flow direction in discrete layouts +}; -/***/ }), -/* 92 */ -/***/ (function(module, exports, __webpack_require__) { +function ConcentricLayout(options) { + this.options = util.extend({}, defaults, options); +} -"use strict"; +ConcentricLayout.prototype.run = function () { + var params = this.options; + var options = params; + var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise; -var math = __webpack_require__(2); -var is = __webpack_require__(0); -var util = __webpack_require__(1); + var cy = params.cy; -var BRp = {}; + var eles = options.eles; + var nodes = eles.nodes().not(':parent'); -BRp.recalculateNodeLabelProjection = function (node) { - var content = node.pstyle('label').strValue; + var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { + x1: 0, y1: 0, w: cy.width(), h: cy.height() + }); - if (is.emptyString(content)) { - return; - } + var center = { + x: bb.x1 + bb.w / 2, + y: bb.y1 + bb.h / 2 + }; - var textX, textY; - var _p = node._private; - var nodeWidth = node.width(); - var nodeHeight = node.height(); - var padding = node.padding(); - var nodePos = node.position(); - var textHalign = node.pstyle('text-halign').strValue; - var textValign = node.pstyle('text-valign').strValue; - var rs = _p.rscratch; - var rstyle = _p.rstyle; + var nodeValues = []; // { node, value } + var theta = options.startAngle; + var maxNodeSize = 0; - switch (textHalign) { - case 'left': - textX = nodePos.x - nodeWidth / 2 - padding; - break; + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var value = void 0; - case 'right': - textX = nodePos.x + nodeWidth / 2 + padding; - break; + // calculate the node value + value = options.concentric(node); + nodeValues.push({ + value: value, + node: node + }); - default: - // e.g. center - textX = nodePos.x; + // for style mapping + node._private.scratch.concentric = value; } - switch (textValign) { - case 'top': - textY = nodePos.y - nodeHeight / 2 - padding; - break; + // in case we used the `concentric` in style + nodes.updateStyle(); - case 'bottom': - textY = nodePos.y + nodeHeight / 2 + padding; - break; + // calculate max size now based on potentially updated mappers + for (var _i = 0; _i < nodes.length; _i++) { + var _node = nodes[_i]; + var nbb = _node.layoutDimensions(options); - default: - // e.g. middle - textY = nodePos.y; + maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h); } - rs.labelX = textX; - rs.labelY = textY; - rstyle.labelX = textX; - rstyle.labelY = textY; - - this.applyLabelDimensions(node); -}; - -BRp.recalculateEdgeLabelProjections = function (edge) { - var p; - var _p = edge._private; - var rs = _p.rscratch; - var r = this; - var content = { - mid: edge.pstyle('label').strValue, - source: edge.pstyle('source-label').strValue, - target: edge.pstyle('target-label').strValue - }; + // sort node values in descreasing order + nodeValues.sort(function (a, b) { + return b.value - a.value; + }); - if (content.mid || content.source || content.target) { - // then we have to calculate... - } else { - return; // no labels => no calcs - } + var levelWidth = options.levelWidth(nodes); - // add center point to style so bounding box calculations can use it - // - p = { - x: rs.midX, - y: rs.midY - }; + // put the values into levels + var levels = [[]]; + var currentLevel = levels[0]; + for (var _i2 = 0; _i2 < nodeValues.length; _i2++) { + var val = nodeValues[_i2]; - var setRs = function setRs(propName, prefix, value) { - util.setPrefixedProperty(_p.rscratch, propName, prefix, value); - util.setPrefixedProperty(_p.rstyle, propName, prefix, value); - }; + if (currentLevel.length > 0) { + var diff = Math.abs(currentLevel[0].value - val.value); - setRs('labelX', null, p.x); - setRs('labelY', null, p.y); + if (diff >= levelWidth) { + currentLevel = []; + levels.push(currentLevel); + } + } - var createControlPointInfo = function createControlPointInfo() { - if (createControlPointInfo.cache) { - return createControlPointInfo.cache; - } // use cache so only 1x per edge + currentLevel.push(val); + } - var ctrlpts = []; + // create positions from levels - // store each ctrlpt info init - for (var i = 0; i + 5 < rs.allpts.length; i += 4) { - var p0 = { x: rs.allpts[i], y: rs.allpts[i + 1] }; - var p1 = { x: rs.allpts[i + 2], y: rs.allpts[i + 3] }; // ctrlpt - var p2 = { x: rs.allpts[i + 4], y: rs.allpts[i + 5] }; + var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes - ctrlpts.push({ - p0: p0, - p1: p1, - p2: p2, - startDist: 0, - length: 0, - segments: [] - }); - } + if (!options.avoidOverlap) { + // then strictly constrain to bb + var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1; + var maxR = Math.min(bb.w, bb.h) / 2 - minDist; + var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0); - var bpts = _p.rstyle.bezierPts; - var nProjs = r.bezierProjPcts.length; + minDist = Math.min(minDist, rStep); + } - function addSegment(cp, p0, p1, t0, t1) { - var length = math.dist(p0, p1); - var prevSegment = cp.segments[cp.segments.length - 1]; - var segment = { - p0: p0, - p1: p1, - t0: t0, - t1: t1, - startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0, - length: length - }; + // find the metrics for each level + var r = 0; + for (var _i3 = 0; _i3 < levels.length; _i3++) { + var level = levels[_i3]; + var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep; + var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); - cp.segments.push(segment); + // calculate the radius + if (level.length > 1 && options.avoidOverlap) { + // but only if more than one node (can't overlap) + var dcos = Math.cos(dTheta) - Math.cos(0); + var dsin = Math.sin(dTheta) - Math.sin(0); + var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping - cp.length += length; + r = Math.max(rMin, r); } - // update each ctrlpt with segment info - for (var i = 0; i < ctrlpts.length; i++) { - var cp = ctrlpts[i]; - var prevCp = ctrlpts[i - 1]; + level.r = r; - if (prevCp) { - cp.startDist = prevCp.startDist + prevCp.length; - } + r += minDist; + } - addSegment(cp, cp.p0, bpts[i * nProjs], 0, r.bezierProjPcts[0]); // first + if (options.equidistant) { + var rDeltaMax = 0; + var _r = 0; - for (var j = 0; j < nProjs - 1; j++) { - addSegment(cp, bpts[i * nProjs + j], bpts[i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]); - } + for (var _i4 = 0; _i4 < levels.length; _i4++) { + var _level = levels[_i4]; + var rDelta = _level.r - _r; - addSegment(cp, bpts[i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last + rDeltaMax = Math.max(rDeltaMax, rDelta); } - return createControlPointInfo.cache = ctrlpts; - }; - - var calculateEndProjection = function calculateEndProjection(prefix) { - var angle; - var isSrc = prefix === 'source'; - - if (!content[prefix]) { - return; - } + _r = 0; + for (var _i5 = 0; _i5 < levels.length; _i5++) { + var _level2 = levels[_i5]; - var offset = edge.pstyle(prefix + '-text-offset').pfValue; + if (_i5 === 0) { + _r = _level2.r; + } - var lineAngle = function lineAngle(p0, p1) { - var dx = p1.x - p0.x; - var dy = p1.y - p0.y; + _level2.r = _r; - return Math.atan(dy / dx); - }; + _r += rDeltaMax; + } + } - var bezierAngle = function bezierAngle(p0, p1, p2, t) { - var t0 = math.bound(0, t - 0.001, 1); - var t1 = math.bound(0, t + 0.001, 1); + // calculate the node positions + var pos = {}; // id => position + for (var _i6 = 0; _i6 < levels.length; _i6++) { + var _level3 = levels[_i6]; + var _dTheta = _level3.dTheta; + var _r2 = _level3.r; - var lp0 = math.qbezierPtAt(p0, p1, p2, t0); - var lp1 = math.qbezierPtAt(p0, p1, p2, t1); + for (var j = 0; j < _level3.length; j++) { + var _val = _level3[j]; + var _theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j; - return lineAngle(lp0, lp1); - }; + var p = { + x: center.x + _r2 * Math.cos(_theta), + y: center.y + _r2 * Math.sin(_theta) + }; - switch (rs.edgeType) { - case 'self': - case 'compound': - case 'bezier': - case 'multibezier': - var cps = createControlPointInfo(); - var selected; - var startDist = 0; - var totalDist = 0; + pos[_val.node.id()] = p; + } + } - // find the segment we're on - for (var i = 0; i < cps.length; i++) { - var cp = cps[isSrc ? i : cps.length - 1 - i]; + // position the nodes + nodes.layoutPositions(this, options, function (ele) { + var id = ele.id(); - for (var j = 0; j < cp.segments.length; j++) { - var seg = cp.segments[isSrc ? j : cp.segments.length - 1 - j]; - var lastSeg = i === cps.length - 1 && j === cp.segments.length - 1; + return pos[id]; + }); - startDist = totalDist; - totalDist += seg.length; + return this; // chaining +}; - if (totalDist >= offset || lastSeg) { - selected = { cp: cp, segment: seg }; - break; - } - } +module.exports = ConcentricLayout; - if (selected) { - break; - } - } +/***/ }), +/* 100 */ +/***/ (function(module, exports, __webpack_require__) { - var cp = selected.cp; - var seg = selected.segment; - var tSegment = (offset - startDist) / seg.length; - var segDt = seg.t1 - seg.t0; - var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment; +"use strict"; - t = math.bound(0, t, 1); - p = math.qbezierPtAt(cp.p0, cp.p1, cp.p2, t); - angle = bezierAngle(cp.p0, cp.p1, cp.p2, t, p); - break; +/* +The CoSE layout was written by Gerardo Huck. +https://www.linkedin.com/in/gerardohuck/ - case 'straight': - case 'segments': - case 'haystack': - var d = 0, - di, - d0; - var p0, p1; - var l = rs.allpts.length; +Based on the following article: +http://dl.acm.org/citation.cfm?id=1498047 - for (var i = 0; i + 3 < l; i += 2) { - if (isSrc) { - p0 = { x: rs.allpts[i], y: rs.allpts[i + 1] }; - p1 = { x: rs.allpts[i + 2], y: rs.allpts[i + 3] }; - } else { - p0 = { x: rs.allpts[l - 2 - i], y: rs.allpts[l - 1 - i] }; - p1 = { x: rs.allpts[l - 4 - i], y: rs.allpts[l - 3 - i] }; - } +Modifications tracked on Github. +*/ - di = math.dist(p0, p1); - d0 = d; - d += di; +var util = __webpack_require__(1); +var math = __webpack_require__(2); +var is = __webpack_require__(0); +var Promise = __webpack_require__(5); - if (d >= offset) { - break; - } - } +var DEBUG; - var pD = offset - d0; - var t = pD / di; +/** + * @brief : default layout options + */ +var defaults = { + // Called on `layoutready` + ready: function ready() {}, - t = math.bound(0, t, 1); - p = math.lineAt(p0, p1, t); - angle = lineAngle(p0, p1); + // Called on `layoutstop` + stop: function stop() {}, - break; - } + // Whether to animate while running the layout + // true : Animate continuously as the layout is running + // false : Just show the end result + // 'end' : Animate with the end result, from the initial positions to the end positions + animate: true, - setRs('labelX', prefix, p.x); - setRs('labelY', prefix, p.y); - setRs('labelAutoAngle', prefix, angle); - }; + // Easing of the animation for animate:'end' + animationEasing: undefined, - calculateEndProjection('source'); - calculateEndProjection('target'); + // The duration of the animation for animate:'end' + animationDuration: undefined, - this.applyLabelDimensions(edge); -}; + // A function that determines whether the node should be animated + // All nodes animated by default on animate enabled + // Non-animated nodes are positioned immediately when the layout starts + animateFilter: function animateFilter(node, i) { + return true; + }, -BRp.applyLabelDimensions = function (ele) { - this.applyPrefixedLabelDimensions(ele); + // The layout animates only after this many milliseconds for animate:true + // (prevents flashing on fast runs) + animationThreshold: 250, - if (ele.isEdge()) { - this.applyPrefixedLabelDimensions(ele, 'source'); - this.applyPrefixedLabelDimensions(ele, 'target'); - } -}; + // Number of iterations between consecutive screen positions update + // (0 -> only updated on the end) + refresh: 20, -BRp.applyPrefixedLabelDimensions = function (ele, prefix) { - var _p = ele._private; + // Whether to fit the network view after when done + fit: true, - var text = this.getLabelText(ele, prefix); - var labelDims = this.calculateLabelDimensions(ele, text); + // Padding on fit + padding: 30, - util.setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, labelDims.width); - util.setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, labelDims.width); + // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } + boundingBox: undefined, - util.setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, labelDims.height); - util.setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, labelDims.height); -}; + // Excludes the label when calculating node bounding boxes for the layout algorithm + nodeDimensionsIncludeLabels: false, -BRp.getLabelText = function (ele, prefix) { - var _p = ele._private; - var pfd = prefix ? prefix + '-' : ''; - var text = ele.pstyle(pfd + 'label').strValue; - var textTransform = ele.pstyle('text-transform').value; - var rscratch = function rscratch(propName, value) { - if (value) { - util.setPrefixedProperty(_p.rscratch, propName, prefix, value); - return value; - } else { - return util.getPrefixedProperty(_p.rscratch, propName, prefix); - } - }; + // Randomize the initial positions of the nodes (true) or use existing positions (false) + randomize: false, - if (textTransform == 'none') { - // passthrough - } else if (textTransform == 'uppercase') { - text = text.toUpperCase(); - } else if (textTransform == 'lowercase') { - text = text.toLowerCase(); - } + // Extra spacing between components in non-compound graphs + componentSpacing: 40, - var wrapStyle = ele.pstyle('text-wrap').value; + // Node repulsion (non overlapping) multiplier + nodeRepulsion: function nodeRepulsion(node) { + return 2048; + }, - if (wrapStyle === 'wrap') { - //console.log('wrap'); + // Node repulsion (overlapping) multiplier + nodeOverlap: 4, - var labelKey = rscratch('labelKey'); + // Ideal edge (non nested) length + idealEdgeLength: function idealEdgeLength(edge) { + return 32; + }, - // save recalc if the label is the same as before - if (labelKey && rscratch('labelWrapKey') === labelKey) { - // console.log('wrap cache hit'); - return rscratch('labelWrapCachedText'); - } - // console.log('wrap cache miss'); + // Divisor to compute edge forces + edgeElasticity: function edgeElasticity(edge) { + return 32; + }, - var lines = text.split('\n'); - var maxW = ele.pstyle('text-max-width').pfValue; - var wrappedLines = []; + // Nesting factor (multiplier) to compute ideal edge length for nested edges + nestingFactor: 1.2, - for (var l = 0; l < lines.length; l++) { - var line = lines[l]; - var lineDims = this.calculateLabelDimensions(ele, line, 'line=' + line); - var lineW = lineDims.width; + // Gravity force (constant) + gravity: 1, - if (lineW > maxW) { - // line is too long - var words = line.split(/\s+/); // NB: assume collapsed whitespace into single space - var subline = ''; + // Maximum number of iterations to perform + numIter: 1000, - for (var w = 0; w < words.length; w++) { - var word = words[w]; - var testLine = subline.length === 0 ? word : subline + ' ' + word; - var testDims = this.calculateLabelDimensions(ele, testLine, 'testLine=' + testLine); - var testW = testDims.width; + // Initial temperature (maximum node displacement) + initialTemp: 1000, - if (testW <= maxW) { - // word fits on current line - subline += word + ' '; - } else { - // word starts new line - wrappedLines.push(subline); - subline = word + ' '; - } - } + // Cooling factor (how the temperature is reduced between consecutive iterations + coolingFactor: 0.99, - // if there's remaining text, put it in a wrapped line - if (!subline.match(/^\s+$/)) { - wrappedLines.push(subline); - } - } else { - // line is already short enough - wrappedLines.push(line); - } - } // for + // Lower temperature threshold (below this point the layout will end) + minTemp: 1.0, - rscratch('labelWrapCachedLines', wrappedLines); - text = rscratch('labelWrapCachedText', wrappedLines.join('\n')); - rscratch('labelWrapKey', labelKey); + // Pass a reference to weaver to use threads for calculations + weaver: false +}; - // console.log(text) - } else if (wrapStyle === 'ellipsis') { - var maxW = ele.pstyle('text-max-width').pfValue; - var ellipsized = ''; - var ellipsis = '\u2026'; - var incLastCh = false; +/** + * @brief : constructor + * @arg options : object containing layout options + */ +function CoseLayout(options) { + this.options = util.extend({}, defaults, options); - for (var i = 0; i < text.length; i++) { - var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width; + this.options.layout = this; +} - if (widthWithNextCh > maxW) { - break; - } +/** + * @brief : runs the layout + */ +CoseLayout.prototype.run = function () { + var options = this.options; + var cy = options.cy; + var layout = this; + var thread = this.thread; + var Thread = options.weaver ? options.weaver.Thread : null; - ellipsized += text[i]; + var falseThread = { // use false thread as polyfill + listeners: [], + on: function on(e, cb) { + this.listeners.push({ event: e, callback: cb }); - if (i === text.length - 1) { - incLastCh = true; + return this; + }, + trigger: function trigger(e) { + if (is.string(e)) { + e = { type: e }; } - } - - if (!incLastCh) { - ellipsized += ellipsis; - } - return ellipsized; - } // if ellipsize + var matchesEvent = function matchesEvent(l) { + return l.event === e.type; + }; + var trigger = function trigger(l) { + l.callback(e); + }; - return text; -}; + this.listeners.filter(matchesEvent).forEach(trigger); -BRp.calculateLabelDimensions = function (ele, text, extraKey) { - var r = this; + return this; + }, + pass: function pass(data) { + this.pass = data; - var cacheKey = ele._private.labelStyleKey + '$@$' + text; + return this; + }, + run: function run(cb) { + var pass = this.pass; - if (extraKey) { - cacheKey += '$@$' + extraKey; - } + return new Promise(function (resolve) { + resolve(cb(pass)); + }); + }, + stop: function stop() { + return this; + } + }; - var cache = r.labelDimCache || (r.labelDimCache = {}); + function broadcast(message) { + // for false thread + var e = { type: 'message', message: message }; - if (cache[cacheKey]) { - return cache[cacheKey]; + falseThread.trigger(e); } - var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text - var fStyle = ele.pstyle('font-style').strValue; - var size = sizeMult * ele.pstyle('font-size').pfValue + 'px'; - var family = ele.pstyle('font-family').strValue; - var weight = ele.pstyle('font-weight').strValue; + if (!thread || thread.stopped()) { + thread = this.thread = Thread ? new Thread() : falseThread; + } - var div = this.labelCalcDiv; + layout.stopped = false; - if (!div) { - div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef - document.body.appendChild(div); // eslint-disable-line no-undef + if (options.animate === true || options.animate === false) { + layout.emit({ type: 'layoutstart', layout: layout }); } - var ds = div.style; - - // from ele style - ds.fontFamily = family; - ds.fontStyle = fStyle; - ds.fontSize = size; - ds.fontWeight = weight; + // Set DEBUG - Global variable + if (true === options.debug) { + DEBUG = true; + } else { + DEBUG = false; + } - // forced style - ds.position = 'absolute'; - ds.left = '-9999px'; - ds.top = '-9999px'; - ds.zIndex = '-1'; - ds.visibility = 'hidden'; - ds.pointerEvents = 'none'; - ds.padding = '0'; - ds.lineHeight = '1'; + // Initialize layout info + var layoutInfo = createLayoutInfo(cy, layout, options); - if (ele.pstyle('text-wrap').value === 'wrap') { - ds.whiteSpace = 'pre'; // so newlines are taken into account - } else { - ds.whiteSpace = 'normal'; + // Show LayoutInfo contents if debugging + if (DEBUG) { + printLayoutInfo(layoutInfo); } - // put label content in div - div.textContent = text; + // If required, randomize node positions + if (options.randomize) { + randomizePositions(layoutInfo, cy); + } - cache[cacheKey] = { - width: Math.ceil(div.clientWidth / sizeMult), - height: Math.ceil(div.clientHeight / sizeMult) - }; + var startTime = Date.now(); + var refreshRequested = false; + var refresh = function refresh(rOpts) { + rOpts = rOpts || {}; - return cache[cacheKey]; -}; + if (refreshRequested && !rOpts.next) { + return; + } -BRp.calculateLabelAngles = function (ele) { - var _p = ele._private; - var rs = _p.rscratch; - var isEdge = ele.isEdge(); - var rot = ele.pstyle('text-rotation'); - var rotStr = rot.strValue; + if (!rOpts.force && Date.now() - startTime < options.animationThreshold) { + return; + } - if (rotStr === 'none') { - rs.labelAngle = rs.sourceLabelAngle = rs.targetLabelAngle = 0; - } else if (isEdge && rotStr === 'autorotate') { - rs.labelAngle = Math.atan(rs.midDispY / rs.midDispX); - rs.sourceLabelAngle = rs.sourceLabelAutoAngle; - rs.targetLabelAngle = rs.targetLabelAutoAngle; - } else if (rotStr === 'autorotate') { - rs.labelAngle = rs.sourceLabelAngle = rs.targetLabelAngle = 0; - } else { - rs.labelAngle = rs.sourceLabelAngle = rs.targetLabelAngle = rot.pfValue; - } -}; + refreshRequested = true; -module.exports = BRp; + util.requestAnimationFrame(function () { + refreshPositions(layoutInfo, cy, options); -/***/ }), -/* 93 */ -/***/ (function(module, exports, __webpack_require__) { + // Fit the graph if necessary + if (true === options.fit) { + cy.fit(options.padding); + } -"use strict"; + refreshRequested = false; + if (rOpts.next) { + rOpts.next(); + } + }); + }; -var BRp = {}; + thread.on('message', function (e) { + var layoutNodes = e.message; -BRp.getNodeShape = function (node) { - var r = this; - var shape = node.pstyle('shape').value; + layoutInfo.layoutNodes = layoutNodes; + refresh(); + }); - if (node.isParent()) { - if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') { - return shape; - } else { - return 'rectangle'; + thread.pass({ + layoutInfo: layoutInfo, + options: { + animate: options.animate, + refresh: options.refresh, + componentSpacing: options.componentSpacing, + nodeOverlap: options.nodeOverlap, + nestingFactor: options.nestingFactor, + gravity: options.gravity, + numIter: options.numIter, + initialTemp: options.initialTemp, + coolingFactor: options.coolingFactor, + minTemp: options.minTemp } - } + }).run(function (pass) { + var layoutInfo = pass.layoutInfo; + var options = pass.options; + var stopped = false; - if (shape === 'polygon') { - var points = node.pstyle('shape-polygon-points').value; + /** + * @brief : Performs one iteration of the physical simulation + * @arg layoutInfo : LayoutInfo object already initialized + * @arg cy : Cytoscape object + * @arg options : Layout options + */ + var step = function step(layoutInfo, options, _step) { + // var s = "\n\n###############################"; + // s += "\nSTEP: " + step; + // s += "\n###############################\n"; + // logDebug(s); - return r.nodeShapes.makePolygon(points).name; - } + // Calculate node repulsions + calculateNodeForces(layoutInfo, options); + // Calculate edge forces + calculateEdgeForces(layoutInfo, options); + // Calculate gravity forces + calculateGravityForces(layoutInfo, options); + // Propagate forces from parent to child + propagateForces(layoutInfo, options); + // Update positions based on calculated forces + updatePositions(layoutInfo, options); + }; - return shape; -}; + /** + * @brief : Computes the node repulsion forces + */ + var calculateNodeForces = function calculateNodeForces(layoutInfo, options) { + // Go through each of the graphs in graphSet + // Nodes only repel each other if they belong to the same graph + // var s = 'calculateNodeForces'; + // logDebug(s); + for (var i = 0; i < layoutInfo.graphSet.length; i++) { + var graph = layoutInfo.graphSet[i]; + var numNodes = graph.length; -module.exports = BRp; + // s = "Set: " + graph.toString(); + // logDebug(s); -/***/ }), -/* 94 */ -/***/ (function(module, exports, __webpack_require__) { + // Now get all the pairs of nodes + // Only get each pair once, (A, B) = (B, A) + for (var j = 0; j < numNodes; j++) { + var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; -"use strict"; + for (var k = j + 1; k < numNodes; k++) { + var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]]; + nodeRepulsion(node1, node2, layoutInfo, options); + } + } + } + }; -var BRp = {}; + var randomDistance = function randomDistance(max) { + return -max + 2 * max * Math.random(); + }; -BRp.registerCalculationListeners = function () { - var cy = this.cy; - var elesToUpdate = cy.collection(); - var r = this; + /** + * @brief : Compute the node repulsion forces between a pair of nodes + */ + var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) { + // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id; - var enqueue = function enqueue(eles, e) { - var dirtyStyleCaches = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; + var cmptId1 = node1.cmptId; + var cmptId2 = node2.cmptId; - elesToUpdate.merge(eles); + if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) { + return; + } - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var _p = ele._private; - var rstyle = _p.rstyle; + // Get direction of line connecting both node centers + var directionX = node2.positionX - node1.positionX; + var directionY = node2.positionY - node1.positionY; + var maxRandDist = 1; + // s += "\ndirectionX: " + directionX + ", directionY: " + directionY; - if (dirtyStyleCaches) { - rstyle.clean = false; - _p.bbCache = null; + // If both centers are the same, apply a random force + if (0 === directionX && 0 === directionY) { + directionX = randomDistance(maxRandDist); + directionY = randomDistance(maxRandDist); } - var evts = rstyle.dirtyEvents = rstyle.dirtyEvents || { length: 0 }; + var overlap = nodesOverlap(node1, node2, directionX, directionY); - if (!evts[e.type]) { - evts[e.type] = true; - evts.length++; - } - } - }; + if (overlap > 0) { + // s += "\nNodes DO overlap."; + // s += "\nOverlap: " + overlap; + // If nodes overlap, repulsion force is proportional + // to the overlap + var force = options.nodeOverlap * overlap; - r.binder(cy) - // nodes + // Compute the module and components of the force vector + var distance = Math.sqrt(directionX * directionX + directionY * directionY); + // s += "\nDistance: " + distance; + var forceX = force * directionX / distance; + var forceY = force * directionY / distance; + } else { + // s += "\nNodes do NOT overlap."; + // If there's no overlap, force is inversely proportional + // to squared distance - .on('position.* style.* free.* bounds.*', 'node', function onDirtyModNode(e) { - var node = e.target; + // Get clipping points for both nodes + var point1 = findClippingPoint(node1, directionX, directionY); + var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); - enqueue(node, e); - enqueue(node.connectedEdges(), e); - }).on('add.*', 'node', function onDirtyAddNode(e) { - var ele = e.target; + // Use clipping points to compute distance + var distanceX = point2.x - point1.x; + var distanceY = point2.y - point1.y; + var distanceSqr = distanceX * distanceX + distanceY * distanceY; + var distance = Math.sqrt(distanceSqr); + // s += "\nDistance: " + distance; - enqueue(ele, e); - }).on('background.*', 'node', function onDirtyBgNode(e) { - var ele = e.target; + // Compute the module and components of the force vector + var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr; + var forceX = force * distanceX / distance; + var forceY = force * distanceY / distance; + } - enqueue(ele, e, false); - }) + // Apply force + if (!node1.isLocked) { + node1.offsetX -= forceX; + node1.offsetY -= forceY; + } - // edges + if (!node2.isLocked) { + node2.offsetX += forceX; + node2.offsetY += forceY; + } - .on('add.* style.*', 'edge', function onDirtyEdge(e) { - var edge = e.target; + // s += "\nForceX: " + forceX + " ForceY: " + forceY; + // logDebug(s); - enqueue(edge, e); - enqueue(edge.parallelEdges(), e); - }).on('remove.*', 'edge', function onDirtyRemoveEdge(e) { - var edge = e.target; - var pEdges = edge.parallelEdges(); + return; + }; - for (var i = 0; i < pEdges.length; i++) { - var pEdge = pEdges[i]; + /** + * @brief : Determines whether two nodes overlap or not + * @return : Amount of overlapping (0 => no overlap) + */ + var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) { - if (!pEdge.removed()) { - enqueue(pEdge, e); + if (dX > 0) { + var overlapX = node1.maxX - node2.minX; + } else { + var overlapX = node2.maxX - node1.minX; } - } - }) - // manual dirtying + if (dY > 0) { + var overlapY = node1.maxY - node2.minY; + } else { + var overlapY = node2.maxY - node1.minY; + } - .on('dirty.*', 'node', function onDirtyEle(e) { - var ele = e.target; + if (overlapX >= 0 && overlapY >= 0) { + return Math.sqrt(overlapX * overlapX + overlapY * overlapY); + } else { + return 0; + } + }; - enqueue(ele, e); - }); + /** + * @brief : Finds the point in which an edge (direction dX, dY) intersects + * the rectangular bounding box of it's source/target node + */ + var findClippingPoint = function findClippingPoint(node, dX, dY) { - var updateEleCalcs = function updateEleCalcs(willDraw) { - if (willDraw) { - var fns = r.onUpdateEleCalcsFns; + // Shorcuts + var X = node.positionX; + var Y = node.positionY; + var H = node.height || 1; + var W = node.width || 1; + var dirSlope = dY / dX; + var nodeSlope = H / W; - if (fns) { - for (var i = 0; i < fns.length; i++) { - var fn = fns[i]; + // var s = 'Computing clipping point of node ' + node.id + + // " . Height: " + H + ", Width: " + W + + // "\nDirection " + dX + ", " + dY; + // + // Compute intersection + var res = {}; - fn(willDraw, elesToUpdate); - } + // Case: Vertical direction (up) + if (0 === dX && 0 < dY) { + res.x = X; + // s += "\nUp direction"; + res.y = Y + H / 2; + + return res; } - r.recalculateRenderedStyle(elesToUpdate, false); + // Case: Vertical direction (down) + if (0 === dX && 0 > dY) { + res.x = X; + res.y = Y + H / 2; + // s += "\nDown direction"; - for (var i = 0; i < elesToUpdate.length; i++) { - elesToUpdate[i]._private.rstyle.dirtyEvents = null; + return res; } - elesToUpdate = cy.collection(); - } - }; + // Case: Intersects the right border + if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) { + res.x = X + W / 2; + res.y = Y + W * dY / 2 / dX; + // s += "\nRightborder"; - r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs); -}; + return res; + } -BRp.onUpdateEleCalcs = function (fn) { - var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || []; + // Case: Intersects the left border + if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) { + res.x = X - W / 2; + res.y = Y - W * dY / 2 / dX; + // s += "\nLeftborder"; - fns.push(fn); -}; + return res; + } -BRp.recalculateRenderedStyle = function (eles, useCache) { - var edges = []; - var nodes = []; + // Case: Intersects the top border + if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) { + res.x = X + H * dX / 2 / dY; + res.y = Y + H / 2; + // s += "\nTop border"; - // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox() - if (this.destroyed) { - return; - } + return res; + } - // use cache by default for perf - if (useCache === undefined) { - useCache = true; - } + // Case: Intersects the bottom border + if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) { + res.x = X - H * dX / 2 / dY; + res.y = Y - H / 2; + // s += "\nBottom border"; - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var _p = ele._private; - var rstyle = _p.rstyle; + return res; + } - // only update if dirty and in graph - if (useCache && rstyle.clean || ele.removed()) { - continue; - } + // s += "\nClipping point found at " + res.x + ", " + res.y; + // logDebug(s); + return res; + }; - // only update if not display: none - if (ele.pstyle('display').value === 'none') { - continue; - } + /** + * @brief : Calculates all edge forces + */ + var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) { + // Iterate over all edges + for (var i = 0; i < layoutInfo.edgeSize; i++) { + // Get edge, source & target nodes + var edge = layoutInfo.layoutEdges[i]; + var sourceIx = layoutInfo.idToIndex[edge.sourceId]; + var source = layoutInfo.layoutNodes[sourceIx]; + var targetIx = layoutInfo.idToIndex[edge.targetId]; + var target = layoutInfo.layoutNodes[targetIx]; - if (_p.group === 'nodes') { - nodes.push(ele); - } else { - // edges - edges.push(ele); - } + // Get direction of line connecting both node centers + var directionX = target.positionX - source.positionX; + var directionY = target.positionY - source.positionY; - rstyle.clean = true; - // rstyle.dirtyEvents = null; - } + // If both centers are the same, do nothing. + // A random force has already been applied as node repulsion + if (0 === directionX && 0 === directionY) { + continue; + } - // update node data from projections - for (var i = 0; i < nodes.length; i++) { - var ele = nodes[i]; - var _p = ele._private; - var rstyle = _p.rstyle; - var pos = ele.position(); + // Get clipping points for both nodes + var point1 = findClippingPoint(source, directionX, directionY); + var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY); - this.recalculateNodeLabelProjection(ele); + var lx = point2.x - point1.x; + var ly = point2.y - point1.y; + var l = Math.sqrt(lx * lx + ly * ly); - rstyle.nodeX = pos.x; - rstyle.nodeY = pos.y; - rstyle.nodeW = ele.pstyle('width').pfValue; - rstyle.nodeH = ele.pstyle('height').pfValue; - } + var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity; - this.recalculateEdgeProjections(edges); + if (0 !== l) { + var forceX = force * lx / l; + var forceY = force * ly / l; + } else { + var forceX = 0; + var forceY = 0; + } - // update edge data from projections - for (var i = 0; i < edges.length; i++) { - var ele = edges[i]; - var _p = ele._private; - var rstyle = _p.rstyle; - var rs = _p.rscratch; + // Add this force to target and source nodes + if (!source.isLocked) { + source.offsetX += forceX; + source.offsetY += forceY; + } - this.recalculateEdgeLabelProjections(ele); + if (!target.isLocked) { + target.offsetX -= forceX; + target.offsetY -= forceY; + } - // update rstyle positions - rstyle.srcX = rs.arrowStartX; - rstyle.srcY = rs.arrowStartY; - rstyle.tgtX = rs.arrowEndX; - rstyle.tgtY = rs.arrowEndY; - rstyle.midX = rs.midX; - rstyle.midY = rs.midY; - rstyle.labelAngle = rs.labelAngle; - rstyle.sourceLabelAngle = rs.sourceLabelAngle; - rstyle.targetLabelAngle = rs.targetLabelAngle; - } -}; + // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id; + // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")"; + // logDebug(s); + } + }; -module.exports = BRp; + /** + * @brief : Computes gravity forces for all nodes + */ + var calculateGravityForces = function calculateGravityForces(layoutInfo, options) { + var distThreshold = 1; -/***/ }), -/* 95 */ -/***/ (function(module, exports, __webpack_require__) { + // var s = 'calculateGravityForces'; + // logDebug(s); + for (var i = 0; i < layoutInfo.graphSet.length; i++) { + var graph = layoutInfo.graphSet[i]; + var numNodes = graph.length; -"use strict"; + // s = "Set: " + graph.toString(); + // logDebug(s); + // Compute graph center + if (0 === i) { + var centerX = layoutInfo.clientHeight / 2; + var centerY = layoutInfo.clientWidth / 2; + } else { + // Get Parent node for this graph, and use its position as center + var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]]; + var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]]; + var centerX = parent.positionX; + var centerY = parent.positionY; + } + // s = "Center found at: " + centerX + ", " + centerY; + // logDebug(s); -var zIndexSort = __webpack_require__(14); + // Apply force to all nodes in graph + for (var j = 0; j < numNodes; j++) { + var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; + // s = "Node: " + node.id; -var BRp = {}; + if (node.isLocked) { + continue; + } -BRp.updateCachedGrabbedEles = function () { - var eles = this.cachedZSortedEles; + var dx = centerX - node.positionX; + var dy = centerY - node.positionY; + var d = Math.sqrt(dx * dx + dy * dy); + if (d > distThreshold) { + var fx = options.gravity * dx / d; + var fy = options.gravity * dy / d; + node.offsetX += fx; + node.offsetY += fy; + // s += ": Applied force: " + fx + ", " + fy; + } else {} + // s += ": skypped since it's too close to center"; - if (!eles) { - // just let this be recalculated on the next z sort tick - return; - } + // logDebug(s); + } + } + }; - eles.drag = []; - eles.nondrag = []; + /** + * @brief : This function propagates the existing offsets from + * parent nodes to its descendents. + * @arg layoutInfo : layoutInfo Object + * @arg cy : cytoscape Object + * @arg options : Layout options + */ + var propagateForces = function propagateForces(layoutInfo, options) { + // Inline implementation of a queue, used for traversing the graph in BFS order + var queue = []; + var start = 0; // Points to the start the queue + var end = -1; // Points to the end of the queue - var grabTargets = []; + // logDebug('propagateForces'); - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var rs = ele._private.rscratch; + // Start by visiting the nodes in the root graph + queue.push.apply(queue, layoutInfo.graphSet[0]); + end += layoutInfo.graphSet[0].length; - if (ele.grabbed() && !ele.isParent()) { - grabTargets.push(ele); - } else if (rs.inDragLayer) { - eles.drag.push(ele); - } else { - eles.nondrag.push(ele); - } - } + // Traverse the graph, level by level, + while (start <= end) { + // Get the node to visit and remove it from queue + var nodeId = queue[start++]; + var nodeIndex = layoutInfo.idToIndex[nodeId]; + var node = layoutInfo.layoutNodes[nodeIndex]; + var children = node.children; - // put the grab target nodes last so it's on top of its neighbourhood - for (var i = 0; i < grabTargets.length; i++) { - var ele = grabTargets[i]; + // We only need to process the node if it's compound + if (0 < children.length && !node.isLocked) { + var offX = node.offsetX; + var offY = node.offsetY; - eles.drag.push(ele); - } -}; + // var s = "Propagating offset from parent node : " + node.id + + // ". OffsetX: " + offX + ". OffsetY: " + offY; + // s += "\n Children: " + children.toString(); + // logDebug(s); -BRp.invalidateCachedZSortedEles = function () { - this.cachedZSortedEles = null; -}; + for (var i = 0; i < children.length; i++) { + var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; + // Propagate offset + childNode.offsetX += offX; + childNode.offsetY += offY; + // Add children to queue to be visited + queue[++end] = children[i]; + } -BRp.getCachedZSortedEles = function (forceRecalc) { - if (forceRecalc || !this.cachedZSortedEles) { - //console.time('cachezorder') + // Reset parent offsets + node.offsetX = 0; + node.offsetY = 0; + } + } + }; - var eles = this.cy.mutableElements().toArray(); + /** + * @brief : Updates the layout model positions, based on + * the accumulated forces + */ + var updatePositions = function updatePositions(layoutInfo, options) { + // var s = 'Updating positions'; + // logDebug(s); - eles.sort(zIndexSort); + // Reset boundaries for compound nodes + for (var i = 0; i < layoutInfo.nodeSize; i++) { + var n = layoutInfo.layoutNodes[i]; + if (0 < n.children.length) { + // logDebug("Resetting boundaries of compound node: " + n.id); + n.maxX = undefined; + n.minX = undefined; + n.maxY = undefined; + n.minY = undefined; + } + } - eles.interactive = eles.filter(function (ele) { - return ele.interactive(); - }); + for (var i = 0; i < layoutInfo.nodeSize; i++) { + var n = layoutInfo.layoutNodes[i]; + if (0 < n.children.length || n.isLocked) { + // No need to set compound or locked node position + // logDebug("Skipping position update of node: " + n.id); + continue; + } + // s = "Node: " + n.id + " Previous position: (" + + // n.positionX + ", " + n.positionY + ")."; - this.cachedZSortedEles = eles; + // Limit displacement in order to improve stability + var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature); + n.positionX += tempForce.x; + n.positionY += tempForce.y; + n.offsetX = 0; + n.offsetY = 0; + n.minX = n.positionX - n.width; + n.maxX = n.positionX + n.width; + n.minY = n.positionY - n.height; + n.maxY = n.positionY + n.height; + // s += " New Position: (" + n.positionX + ", " + n.positionY + ")."; + // logDebug(s); - this.updateCachedGrabbedEles(); - } else { - eles = this.cachedZSortedEles; - } + // Update ancestry boudaries + updateAncestryBoundaries(n, layoutInfo); + } - return eles; -}; + // Update size, position of compund nodes + for (var i = 0; i < layoutInfo.nodeSize; i++) { + var n = layoutInfo.layoutNodes[i]; + if (0 < n.children.length && !n.isLocked) { + n.positionX = (n.maxX + n.minX) / 2; + n.positionY = (n.maxY + n.minY) / 2; + n.width = n.maxX - n.minX; + n.height = n.maxY - n.minY; + // s = "Updating position, size of compound node " + n.id; + // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY; + // s += "\nWidth: " + n.width + ", Height: " + n.height; + // logDebug(s); + } + } + }; -module.exports = BRp; + /** + * @brief : Limits a force (forceX, forceY) to be not + * greater (in modulo) than max. + 8 Preserves force direction. + */ + var limitForce = function limitForce(forceX, forceY, max) { + // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max; + var force = Math.sqrt(forceX * forceX + forceY * forceY); -/***/ }), -/* 96 */ -/***/ (function(module, exports, __webpack_require__) { + if (force > max) { + var res = { + x: max * forceX / force, + y: max * forceY / force + }; + } else { + var res = { + x: forceX, + y: forceY + }; + } -"use strict"; + // s += ".\nResult: (" + res.x + ", " + res.y + ")"; + // logDebug(s); + return res; + }; -var BRp = {}; + /** + * @brief : Function used for keeping track of compound node + * sizes, since they should bound all their subnodes. + */ + var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) { + // var s = "Propagating new position/size of node " + node.id; + var parentId = node.parentId; + if (null == parentId) { + // If there's no parent, we are done + // s += ". No parent node."; + // logDebug(s); + return; + } -BRp.getCachedImage = function (url, crossOrigin, onLoad) { - var r = this; - var imageCache = r.imageCache = r.imageCache || {}; - var cache = imageCache[url]; + // Get Parent Node + var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]]; + var flag = false; - if (cache) { - if (!cache.image.complete) { - cache.image.addEventListener('load', onLoad); - } + // MaxX + if (null == p.maxX || node.maxX + p.padRight > p.maxX) { + p.maxX = node.maxX + p.padRight; + flag = true; + // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX; + } - return cache.image; - } else { - cache = imageCache[url] = imageCache[url] || {}; + // MinX + if (null == p.minX || node.minX - p.padLeft < p.minX) { + p.minX = node.minX - p.padLeft; + flag = true; + // s += "\nNew minX for parent node " + p.id + ": " + p.minX; + } - var image = cache.image = new Image(); // eslint-disable-line no-undef + // MaxY + if (null == p.maxY || node.maxY + p.padBottom > p.maxY) { + p.maxY = node.maxY + p.padBottom; + flag = true; + // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY; + } - image.addEventListener('load', onLoad); - image.addEventListener('error', function () { - image.error = true; - }); + // MinY + if (null == p.minY || node.minY - p.padTop < p.minY) { + p.minY = node.minY - p.padTop; + flag = true; + // s += "\nNew minY for parent node " + p.id + ": " + p.minY; + } - // #1582 safari doesn't load data uris with crossOrigin properly - // https://bugs.webkit.org/show_bug.cgi?id=123978 - var dataUriPrefix = 'data:'; - var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix; - if (!isDataUri) { - image.crossOrigin = crossOrigin; // prevent tainted canvas - } + // If updated boundaries, propagate changes upward + if (flag) { + // logDebug(s); + return updateAncestryBoundaries(p, layoutInfo); + } - image.src = url; + // s += ". No changes in boundaries/position of parent node " + p.id; + // logDebug(s); + return; + }; - return image; - } -}; + var separateComponents = function separateComponents(layutInfo, options) { + var nodes = layoutInfo.layoutNodes; + var components = []; -module.exports = BRp; + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var cid = node.cmptId; + var component = components[cid] = components[cid] || []; -/***/ }), -/* 97 */ -/***/ (function(module, exports, __webpack_require__) { + component.push(node); + } -"use strict"; + var totalA = 0; + for (var i = 0; i < components.length; i++) { + var c = components[i]; -var is = __webpack_require__(0); -var util = __webpack_require__(1); -var window = __webpack_require__(4); + if (!c) { + continue; + } -var BaseRenderer = function BaseRenderer(options) { - this.init(options); -}; -var BR = BaseRenderer; -var BRp = BR.prototype; + c.x1 = Infinity; + c.x2 = -Infinity; + c.y1 = Infinity; + c.y2 = -Infinity; -BRp.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl']; + for (var j = 0; j < c.length; j++) { + var n = c[j]; -BRp.init = function (options) { - var r = this; + c.x1 = Math.min(c.x1, n.positionX - n.width / 2); + c.x2 = Math.max(c.x2, n.positionX + n.width / 2); + c.y1 = Math.min(c.y1, n.positionY - n.height / 2); + c.y2 = Math.max(c.y2, n.positionY + n.height / 2); + } - r.options = options; + c.w = c.x2 - c.x1; + c.h = c.y2 - c.y1; - r.cy = options.cy; + totalA += c.w * c.h; + } - var ctr = r.container = options.cy.container(); + components.sort(function (c1, c2) { + return c2.w * c2.h - c1.w * c1.h; + }); - // prepend a stylesheet in the head such that - if (window) { - var document = window.document; - var head = document.head; - var stylesheetId = '__________cytoscape_stylesheet'; - var className = '__________cytoscape_container'; - var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null; + var x = 0; + var y = 0; + var usedW = 0; + var rowH = 0; + var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight; - if (ctr.className.indexOf(className) < 0) { - ctr.className = (ctr.className || '') + ' ' + className; - } + for (var i = 0; i < components.length; i++) { + var c = components[i]; - if (!stylesheetAlreadyExists) { - var stylesheet = document.createElement('style'); + if (!c) { + continue; + } - stylesheet.id = stylesheetId; - stylesheet.innerHTML = '.' + className + ' { position: relative; }'; + for (var j = 0; j < c.length; j++) { + var n = c[j]; - head.insertBefore(stylesheet, head.children[0]); // first so lowest priority - } + if (!n.isLocked) { + n.positionX += x; + n.positionY += y; + } + } - var computedStyle = window.getComputedStyle(ctr); - var position = computedStyle.getPropertyValue('position'); + x += c.w + options.componentSpacing; + usedW += c.w + options.componentSpacing; + rowH = Math.max(rowH, c.h); - if (position === 'static') { - util.error('A Cytoscape container has style position:static and so can not use UI extensions properly'); - } - } + if (usedW > maxRowW) { + y += rowH + options.componentSpacing; + x = 0; + usedW = 0; + rowH = 0; + } + } + }; - r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag + var mainLoop = function mainLoop(i) { + if (stopped) { + // logDebug("Layout manually stopped. Stopping computation in step " + i); + return false; + } - r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; + // Do one step in the phisical simulation + step(layoutInfo, options, i); - //--Pointer-related data - r.hoverData = { down: null, last: null, - downTime: null, triggerMode: null, - dragging: false, - initialPan: [null, null], capture: false }; + // Update temperature + layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; + // logDebug("New temperature: " + layoutInfo.temperature); - r.dragData = { possibleDragElements: [] }; + if (layoutInfo.temperature < options.minTemp) { + // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i); + return false; + } - r.touchData = { - start: null, capture: false, + return true; + }; - // These 3 fields related to tap, taphold events - startPosition: [null, null, null, null, null, null], - singleTouchStartTime: null, - singleTouchMoved: true, + var i = 0; + var loopRet; - now: [null, null, null, null, null, null], - earlier: [null, null, null, null, null, null] - }; + do { + var f = 0; - r.redraws = 0; - r.showFps = options.showFps; - r.debug = options.debug; + while (f < options.refresh && i < options.numIter) { + var loopRet = mainLoop(i); + if (!loopRet) { + break; + } - r.hideEdgesOnViewport = options.hideEdgesOnViewport; - r.hideLabelsOnViewport = options.hideLabelsOnViewport; - r.textureOnViewport = options.textureOnViewport; - r.wheelSensitivity = options.wheelSensitivity; - r.motionBlurEnabled = options.motionBlur; // on by default - r.forcedPixelRatio = options.pixelRatio; - r.motionBlur = options.motionBlur; // for initial kick off - r.motionBlurOpacity = options.motionBlurOpacity; - r.motionBlurTransparency = 1 - r.motionBlurOpacity; - r.motionBlurPxRatio = 1; - r.mbPxRBlurry = 1; //0.8; - r.minMbLowQualFrames = 4; - r.fullQualityMb = false; - r.clearedForMotionBlur = []; - r.desktopTapThreshold = options.desktopTapThreshold; - r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold; - r.touchTapThreshold = options.touchTapThreshold; - r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold; - r.tapholdDuration = 500; + f++; + i++; + } - r.bindings = []; - r.beforeRenderCallbacks = []; - r.beforeRenderPriorities = { // higher priority execs before lower one - animations: 400, - eleCalcs: 300, - eleTxrDeq: 200, - lyrTxrDeq: 100 - }; + if (options.animate === true) { + broadcast(layoutInfo.layoutNodes); // eslint-disable-line no-undef + } + } while (loopRet && i + 1 < options.numIter); - r.registerNodeShapes(); - r.registerArrowShapes(); - r.registerCalculationListeners(); -}; + separateComponents(layoutInfo, options); -BRp.notify = function (params) { - var types; - var r = this; + return layoutInfo; + }).then(function (layoutInfoUpdated) { + layoutInfo.layoutNodes = layoutInfoUpdated.layoutNodes; // get the positions - // the renderer can't be notified after it's destroyed - if (this.destroyed) { - return; - } + thread.stop(); + done(); + }); - if (is.array(params.type)) { - types = params.type; - } else { - types = [params.type]; - } + var done = function done() { + if (options.animate === true || options.animate === false) { + refresh({ + force: true, + next: function next() { + // Layout has finished + layout.one('layoutstop', options.stop); + layout.emit({ type: 'layoutstop', layout: layout }); + } + }); + } else { + options.eles.nodes().layoutPositions(layout, options, function (node) { + var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]]; - var has = {}; - for (var i = 0; i < types.length; i++) { - var type = types[i]; + return { x: lnode.positionX, y: lnode.positionY }; + }); + } + }; - has[type] = true; - } // for + return this; // chaining +}; - if (has['init']) { - r.load(); - return; - } +/** + * @brief : called on continuous layouts to stop them before they finish + */ +CoseLayout.prototype.stop = function () { + this.stopped = true; - if (has['destroy']) { - r.destroy(); - return; + if (this.thread) { + this.thread.stop(); } - if (has['add'] || has['remove'] || has['load'] || has['zorder']) { - r.invalidateCachedZSortedEles(); - } + this.emit('layoutstop'); - if (has['viewport']) { - r.redrawHint('select', true); - } + return this; // chaining +}; - if (has['load'] || has['resize']) { - r.invalidateContainerClientCoordsCache(); - r.matchCanvasSize(r.container); +CoseLayout.prototype.destroy = function () { + if (this.thread) { + this.thread.stop(); } - r.redrawHint('eles', true); - r.redrawHint('drag', true); - - this.startRenderLoop(); - - this.redraw(); + return this; // chaining }; -BRp.destroy = function () { - var r = this; - - r.destroyed = true; +/** + * @brief : Creates an object which is contains all the data + * used in the layout process + * @arg cy : cytoscape.js object + * @return : layoutInfo object initialized + */ +var createLayoutInfo = function createLayoutInfo(cy, layout, options) { + // Shortcut + var edges = options.eles.edges(); + var nodes = options.eles.nodes(); - r.cy.stopAnimationLoop(); + var layoutInfo = { + isCompound: cy.hasCompoundNodes(), + layoutNodes: [], + idToIndex: {}, + nodeSize: nodes.size(), + graphSet: [], + indexToGraph: [], + layoutEdges: [], + edgeSize: edges.size(), + temperature: options.initialTemp, + clientWidth: cy.width(), + clientHeight: cy.width(), + boundingBox: math.makeBoundingBox(options.boundingBox ? options.boundingBox : { + x1: 0, y1: 0, w: cy.width(), h: cy.height() + }) + }; - for (var i = 0; i < r.bindings.length; i++) { - var binding = r.bindings[i]; - var b = binding; - var tgt = b.target; + var components = options.eles.components(); + var id2cmptId = {}; - (tgt.off || tgt.removeEventListener).apply(tgt, b.args); - } + for (var i = 0; i < components.length; i++) { + var component = components[i]; - r.bindings = []; - r.beforeRenderCallbacks = []; - r.onUpdateEleCalcsFns = []; + for (var j = 0; j < component.length; j++) { + var node = component[j]; - if (r.removeObserver) { - r.removeObserver.disconnect(); + id2cmptId[node.id()] = i; + } } - if (r.styleObserver) { - r.styleObserver.disconnect(); - } + // Iterate over all nodes, creating layout nodes + for (var i = 0; i < layoutInfo.nodeSize; i++) { + var n = nodes[i]; + var nbb = n.layoutDimensions(options); - if (r.labelCalcDiv) { - try { - document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef - } catch (e) { - // ie10 issue #1014 - } - } -}; + var tempNode = {}; + tempNode.isLocked = n.locked(); + tempNode.id = n.data('id'); + tempNode.parentId = n.data('parent'); + tempNode.cmptId = id2cmptId[n.id()]; + tempNode.children = []; + tempNode.positionX = n.position('x'); + tempNode.positionY = n.position('y'); + tempNode.offsetX = 0; + tempNode.offsetY = 0; + tempNode.height = nbb.w; + tempNode.width = nbb.h; + tempNode.maxX = tempNode.positionX + tempNode.width / 2; + tempNode.minX = tempNode.positionX - tempNode.width / 2; + tempNode.maxY = tempNode.positionY + tempNode.height / 2; + tempNode.minY = tempNode.positionY - tempNode.height / 2; + tempNode.padLeft = parseFloat(n.style('padding')); + tempNode.padRight = parseFloat(n.style('padding')); + tempNode.padTop = parseFloat(n.style('padding')); + tempNode.padBottom = parseFloat(n.style('padding')); -[__webpack_require__(85), __webpack_require__(91), __webpack_require__(96), __webpack_require__(98), __webpack_require__(99), __webpack_require__(100)].forEach(function (props) { - util.extend(BRp, props); -}); + // forces + tempNode.nodeRepulsion = is.fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; -module.exports = BR; + // Add new node + layoutInfo.layoutNodes.push(tempNode); + // Add entry to id-index map + layoutInfo.idToIndex[tempNode.id] = i; + } -/***/ }), -/* 98 */ -/***/ (function(module, exports, __webpack_require__) { + // Inline implementation of a queue, used for traversing the graph in BFS order + var queue = []; + var start = 0; // Points to the start the queue + var end = -1; // Points to the end of the queue -"use strict"; + var tempGraph = []; + // Second pass to add child information and + // initialize queue for hierarchical traversal + for (var i = 0; i < layoutInfo.nodeSize; i++) { + var n = layoutInfo.layoutNodes[i]; + var p_id = n.parentId; + // Check if node n has a parent node + if (null != p_id) { + // Add node Id to parent's list of children + layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id); + } else { + // If a node doesn't have a parent, then it's in the root graph + queue[++end] = n.id; + tempGraph.push(n.id); + } + } -var is = __webpack_require__(0); -var util = __webpack_require__(1); -var math = __webpack_require__(2); -var Event = __webpack_require__(15); + // Add root graph to graphSet + layoutInfo.graphSet.push(tempGraph); -var BRp = {}; + // Traverse the graph, level by level, + while (start <= end) { + // Get the node to visit and remove it from queue + var node_id = queue[start++]; + var node_ix = layoutInfo.idToIndex[node_id]; + var node = layoutInfo.layoutNodes[node_ix]; + var children = node.children; + if (children.length > 0) { + // Add children nodes as a new graph to graph set + layoutInfo.graphSet.push(children); + // Add children to que queue to be visited + for (var i = 0; i < children.length; i++) { + queue[++end] = children[i]; + } + } + } -BRp.registerBinding = function (target, event, handler, useCapture) { - var args = Array.prototype.slice.apply(arguments, [1]); // copy - var b = this.binder(target); + // Create indexToGraph map + for (var i = 0; i < layoutInfo.graphSet.length; i++) { + var graph = layoutInfo.graphSet[i]; + for (var j = 0; j < graph.length; j++) { + var index = layoutInfo.idToIndex[graph[j]]; + layoutInfo.indexToGraph[index] = i; + } + } - return b.on.apply(b, args); -}; + // Iterate over all edges, creating Layout Edges + for (var i = 0; i < layoutInfo.edgeSize; i++) { + var e = edges[i]; + var tempEdge = {}; + tempEdge.id = e.data('id'); + tempEdge.sourceId = e.data('source'); + tempEdge.targetId = e.data('target'); -BRp.binder = function (tgt) { - var r = this; + // Compute ideal length + var idealLength = is.fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength; + var elasticity = is.fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; - var tgtIsDom = tgt === window || tgt === document || tgt === document.body || is.domElement(tgt); + // Check if it's an inter graph edge + var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId]; + var targetIx = layoutInfo.idToIndex[tempEdge.targetId]; + var sourceGraph = layoutInfo.indexToGraph[sourceIx]; + var targetGraph = layoutInfo.indexToGraph[targetIx]; - if (r.supportsPassiveEvents == null) { + if (sourceGraph != targetGraph) { + // Find lowest common graph ancestor + var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); - // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection - var supportsPassive = false; - try { - var opts = Object.defineProperty({}, 'passive', { - get: function get() { - supportsPassive = true; - } - }); + // Compute sum of node depths, relative to lca graph + var lcaGraph = layoutInfo.graphSet[lca]; + var depth = 0; - window.addEventListener('test', null, opts); - } catch (err) {} + // Source depth + var tempNode = layoutInfo.layoutNodes[sourceIx]; + while (-1 === lcaGraph.indexOf(tempNode.id)) { + tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]]; + depth++; + } - r.supportsPassiveEvents = supportsPassive; - } + // Target depth + tempNode = layoutInfo.layoutNodes[targetIx]; + while (-1 === lcaGraph.indexOf(tempNode.id)) { + tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]]; + depth++; + } - var on = function on(event, handler, useCapture) { - var args = Array.prototype.slice.call(arguments); + // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId + + // ". Index: " + lca + " Contents: " + lcaGraph.toString() + + // ". Depth: " + depth); - if (tgtIsDom && r.supportsPassiveEvents) { - // replace useCapture w/ opts obj - args[2] = { - capture: useCapture != null ? useCapture : false, - passive: false, - once: false - }; + // Update idealLength + idealLength *= depth * options.nestingFactor; } - r.bindings.push({ - target: tgt, - args: args - }); - - (tgt.addEventListener || tgt.on).apply(tgt, args); - - return this; - }; + tempEdge.idealLength = idealLength; + tempEdge.elasticity = elasticity; - return { - on: on, - addEventListener: on, - addListener: on, - bind: on - }; -}; + layoutInfo.layoutEdges.push(tempEdge); + } -BRp.nodeIsDraggable = function (node) { - return node && node.isNode() && !node.locked() && node.grabbable(); + // Finally, return layoutInfo object + return layoutInfo; }; -BRp.nodeIsGrabbable = function (node) { - return this.nodeIsDraggable(node) && node.interactive(); +/** + * @brief : This function finds the index of the lowest common + * graph ancestor between 2 nodes in the subtree + * (from the graph hierarchy induced tree) whose + * root is graphIx + * + * @arg node1: node1's ID + * @arg node2: node2's ID + * @arg layoutInfo: layoutInfo object + * + */ +var findLCA = function findLCA(node1, node2, layoutInfo) { + // Find their common ancester, starting from the root graph + var res = findLCA_aux(node1, node2, 0, layoutInfo); + if (2 > res.count) { + // If aux function couldn't find the common ancester, + // then it is the root graph + return 0; + } else { + return res.graph; + } }; -BRp.load = function () { - var r = this; - - var triggerEvents = function triggerEvents(target, names, e, props) { - if (target == null) { - target = r.cy; - } +/** + * @brief : Auxiliary function used for LCA computation + * + * @arg node1 : node1's ID + * @arg node2 : node2's ID + * @arg graphIx : subgraph index + * @arg layoutInfo : layoutInfo object + * + * @return : object of the form {count: X, graph: Y}, where: + * X is the number of ancesters (max: 2) found in + * graphIx (and it's subgraphs), + * Y is the graph index of the lowest graph containing + * all X nodes + */ +var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) { + var graph = layoutInfo.graphSet[graphIx]; + // If both nodes belongs to graphIx + if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) { + return { count: 2, graph: graphIx }; + } - for (var i = 0; i < names.length; i++) { - var name = names[i]; + // Make recursive calls for all subgraphs + var c = 0; + for (var i = 0; i < graph.length; i++) { + var nodeId = graph[i]; + var nodeIx = layoutInfo.idToIndex[nodeId]; + var children = layoutInfo.layoutNodes[nodeIx].children; - target.emit(util.extend({ originalEvent: e, type: name }, props)); + // If the node has no child, skip it + if (0 === children.length) { + continue; } - }; - - var isMultSelKeyDown = function isMultSelKeyDown(e) { - return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey - }; - var allowPanningPassthrough = function allowPanningPassthrough(down, downs) { - var allowPassthrough = true; - - if (r.cy.hasCompoundNodes() && down && down.isEdge()) { - // a compound node below the edge => no passthrough panning - for (var i = 0; downs && i < downs.length; i++) { - var down = downs[i]; - - if (down.isNode() && down.isParent()) { - allowPassthrough = false; - break; - } + var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]]; + var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo); + if (0 === result.count) { + // Neither node1 nor node2 are present in this subgraph + continue; + } else if (1 === result.count) { + // One of (node1, node2) is present in this subgraph + c++; + if (2 === c) { + // We've already found both nodes, no need to keep searching + break; } } else { - allowPassthrough = true; + // Both nodes are present in this subgraph + return result; } + } - return allowPassthrough; - }; - - var getDragListIds = function getDragListIds(opts) { - var listHasId; - - if (opts.addToList && r.cy.hasCompoundNodes()) { - // only needed for compound graphs - if (!opts.addToList.hasId) { - // build ids lookup if doesn't already exist - opts.addToList.hasId = {}; - - for (var i = 0; i < opts.addToList.length; i++) { - var ele = opts.addToList[i]; + return { count: c, graph: graphIx }; +}; - opts.addToList.hasId[ele.id()] = true; - } - } +/** + * @brief: printsLayoutInfo into js console + * Only used for debbuging + */ +var printLayoutInfo = function printLayoutInfo(layoutInfo) { + /* eslint-disable */ - listHasId = opts.addToList.hasId; - } + if (!DEBUG) { + return; + } + console.debug('layoutNodes:'); + for (var i = 0; i < layoutInfo.nodeSize; i++) { + var n = layoutInfo.layoutNodes[i]; + var s = '\nindex: ' + i + '\nId: ' + n.id + '\nChildren: ' + n.children.toString() + '\nparentId: ' + n.parentId + '\npositionX: ' + n.positionX + '\npositionY: ' + n.positionY + '\nOffsetX: ' + n.offsetX + '\nOffsetY: ' + n.offsetY + '\npadLeft: ' + n.padLeft + '\npadRight: ' + n.padRight + '\npadTop: ' + n.padTop + '\npadBottom: ' + n.padBottom; - return listHasId || {}; - }; + console.debug(s); + } - var setGrabbed = function setGrabbed(ele) { - ele[0]._private.grabbed = true; - }; + console.debug('idToIndex'); + for (var i in layoutInfo.idToIndex) { + console.debug('Id: ' + i + '\nIndex: ' + layoutInfo.idToIndex[i]); + } - var setFreed = function setFreed(ele) { - ele[0]._private.grabbed = false; - }; + console.debug('Graph Set'); + var set = layoutInfo.graphSet; + for (var i = 0; i < set.length; i++) { + console.debug('Set : ' + i + ': ' + set[i].toString()); + } - var setInDragLayer = function setInDragLayer(ele) { - ele[0]._private.rscratch.inDragLayer = true; - }; + var s = 'IndexToGraph'; + for (var i = 0; i < layoutInfo.indexToGraph.length; i++) { + s += '\nIndex : ' + i + ' Graph: ' + layoutInfo.indexToGraph[i]; + } + console.debug(s); - var setOutDragLayer = function setOutDragLayer(ele) { - ele[0]._private.rscratch.inDragLayer = false; - }; + s = 'Layout Edges'; + for (var i = 0; i < layoutInfo.layoutEdges.length; i++) { + var e = layoutInfo.layoutEdges[i]; + s += '\nEdge Index: ' + i + ' ID: ' + e.id + ' SouceID: ' + e.sourceId + ' TargetId: ' + e.targetId + ' Ideal Length: ' + e.idealLength; + } + console.debug(s); - var setGrabTarget = function setGrabTarget(ele) { - ele[0]._private.rscratch.isGrabTarget = true; - }; + s = 'nodeSize: ' + layoutInfo.nodeSize; + s += '\nedgeSize: ' + layoutInfo.edgeSize; + s += '\ntemperature: ' + layoutInfo.temperature; + console.debug(s); - var removeGrabTarget = function removeGrabTarget(ele) { - ele[0]._private.rscratch.isGrabTarget = false; - }; + return; + /* eslint-enable */ +}; - var addToDragList = function addToDragList(ele, opts) { - var listHasId = getDragListIds(opts); +/** + * @brief : Randomizes the position of all nodes + */ +var randomizePositions = function randomizePositions(layoutInfo, cy) { + var width = layoutInfo.clientWidth; + var height = layoutInfo.clientHeight; - if (!listHasId[ele.id()]) { - opts.addToList.push(ele); - listHasId[ele.id()] = true; + for (var i = 0; i < layoutInfo.nodeSize; i++) { + var n = layoutInfo.layoutNodes[i]; - setGrabbed(ele); + // No need to randomize compound nodes or locked nodes + if (0 === n.children.length && !n.isLocked) { + n.positionX = Math.random() * width; + n.positionY = Math.random() * height; } - }; + } +}; - // helper function to determine which child nodes and inner edges - // of a compound node to be dragged as well as the grabbed and selected nodes - var addDescendantsToDrag = function addDescendantsToDrag(node, opts) { - if (!node.cy().hasCompoundNodes()) { - return; - } +/** + * @brief : Updates the positions of nodes in the network + * @arg layoutInfo : LayoutInfo object + * @arg cy : Cytoscape object + * @arg options : Layout options + */ +var refreshPositions = function refreshPositions(layoutInfo, cy, options) { + // var s = 'Refreshing positions'; + // logDebug(s); - if (opts.inDragLayer == null && opts.addToList == null) { - return; - } // nothing to do + var layout = options.layout; + var nodes = options.eles.nodes(); + var bb = layoutInfo.boundingBox; + var coseBB = { x1: Infinity, x2: -Infinity, y1: Infinity, y2: -Infinity }; - var innerNodes = node.descendants(); + if (options.boundingBox) { + nodes.forEach(function (node) { + var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]]; - if (opts.inDragLayer) { - innerNodes.forEach(setInDragLayer); - innerNodes.connectedEdges().forEach(setInDragLayer); - } + coseBB.x1 = Math.min(coseBB.x1, lnode.positionX); + coseBB.x2 = Math.max(coseBB.x2, lnode.positionX); - if (opts.addToList) { - innerNodes.forEach(function (ele) { - addToDragList(ele, opts); - }); - } - }; + coseBB.y1 = Math.min(coseBB.y1, lnode.positionY); + coseBB.y2 = Math.max(coseBB.y2, lnode.positionY); + }); - // adds the given nodes and its neighbourhood to the drag layer - var addNodesToDrag = function addNodesToDrag(nodes, opts) { - opts = opts || {}; + coseBB.w = coseBB.x2 - coseBB.x1; + coseBB.h = coseBB.y2 - coseBB.y1; + } - var hasCompoundNodes = nodes.cy().hasCompoundNodes(); + nodes.positions(function (ele, i) { + var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]]; + // s = "Node: " + lnode.id + ". Refreshed position: (" + + // lnode.positionX + ", " + lnode.positionY + ")."; + // logDebug(s); - if (opts.inDragLayer) { - nodes.forEach(setInDragLayer); + if (options.boundingBox) { + // then add extra bounding box constraint + var pctX = (lnode.positionX - coseBB.x1) / coseBB.w; + var pctY = (lnode.positionY - coseBB.y1) / coseBB.h; - nodes.neighborhood().stdFilter(function (ele) { - return !hasCompoundNodes || ele.isEdge(); - }).forEach(setInDragLayer); + return { + x: bb.x1 + pctX * bb.w, + y: bb.y1 + pctY * bb.h + }; + } else { + return { + x: lnode.positionX, + y: lnode.positionY + }; } + }); - if (opts.addToList) { - nodes.forEach(function (ele) { - addToDragList(ele, opts); - }); - } + // Trigger layoutReady only on first call + if (true !== layoutInfo.ready) { + // s = 'Triggering layoutready'; + // logDebug(s); + layoutInfo.ready = true; + layout.one('layoutready', options.ready); + layout.emit({ type: 'layoutready', layout: this }); + } +}; - addDescendantsToDrag(nodes, opts); // always add to drag +/** + * @brief : Logs a debug message in JS console, if DEBUG is ON + */ +// var logDebug = function(text) { +// if (DEBUG) { +// console.debug(text); +// } +// }; - // also add nodes and edges related to the topmost ancestor - updateAncestorsInDragLayer(nodes, { - inDragLayer: opts.inDragLayer - }); +module.exports = CoseLayout; - r.updateCachedGrabbedEles(); - }; +/***/ }), +/* 101 */ +/***/ (function(module, exports, __webpack_require__) { - var addNodeToDrag = addNodesToDrag; +"use strict"; - var freeDraggedElements = function freeDraggedElements(grabbedEles) { - if (!grabbedEles) { - return; - } - grabbedEles.hasId = {}; // clear the id list +var util = __webpack_require__(1); +var math = __webpack_require__(2); - // just go over all elements rather than doing a bunch of (possibly expensive) traversals - r.getCachedZSortedEles().forEach(function (ele) { - setFreed(ele); - setOutDragLayer(ele); - removeGrabTarget(ele); - }); +var defaults = { + fit: true, // whether to fit the viewport to the graph + padding: 30, // padding used on fit + boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } + avoidOverlap: true, // prevents node overlap, may overflow boundingBox if not enough space + avoidOverlapPadding: 10, // extra spacing around nodes when avoidOverlap: true + nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm + spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up + condense: false, // uses all available space on false, uses minimal space on true + rows: undefined, // force num of rows in the grid + cols: undefined, // force num of columns in the grid + position: function position(node) {}, // returns { row, col } for element + sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') } + animate: false, // whether to transition the node positions + animationDuration: 500, // duration of animation in ms if enabled + animationEasing: undefined, // easing of animation if enabled + animateFilter: function animateFilter(node, i) { + return true; + }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts + ready: undefined, // callback on layoutready + stop: undefined, // callback on layoutstop + transform: function transform(node, position) { + return position; + } // transform a given node position. Useful for changing flow direction in discrete layouts +}; - r.updateCachedGrabbedEles(); - }; +function GridLayout(options) { + this.options = util.extend({}, defaults, options); +} - // helper function to determine which ancestor nodes and edges should go - // to the drag layer (or should be removed from drag layer). - var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) { +GridLayout.prototype.run = function () { + var params = this.options; + var options = params; - if (opts.inDragLayer == null && opts.addToList == null) { - return; - } // nothing to do + var cy = params.cy; + var eles = options.eles; + var nodes = eles.nodes().not(':parent'); - if (!node.cy().hasCompoundNodes()) { - return; - } + if (options.sort) { + nodes = nodes.sort(options.sort); + } + + var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { + x1: 0, y1: 0, w: cy.width(), h: cy.height() + }); + + if (bb.h === 0 || bb.w === 0) { + nodes.layoutPositions(this, options, function (ele) { + return { x: bb.x1, y: bb.y1 }; + }); + } else { - // find top-level parent - var parent = node.ancestors().orphans(); + // width/height * splits^2 = cells where splits is number of times to split width + var cells = nodes.size(); + var splits = Math.sqrt(cells * bb.h / bb.w); + var rows = Math.round(splits); + var cols = Math.round(bb.w / bb.h * splits); - // no parent node: no nodes to add to the drag layer - if (parent.same(node)) { - return; - } + var small = function small(val) { + if (val == null) { + return Math.min(rows, cols); + } else { + var min = Math.min(rows, cols); + if (min == rows) { + rows = val; + } else { + cols = val; + } + } + }; - var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants()); + var large = function large(val) { + if (val == null) { + return Math.max(rows, cols); + } else { + var max = Math.max(rows, cols); + if (max == rows) { + rows = val; + } else { + cols = val; + } + } + }; - var edges = nodes.connectedEdges(); + var oRows = options.rows; + var oCols = options.cols != null ? options.cols : options.columns; - if (opts.inDragLayer) { - edges.forEach(setInDragLayer); - nodes.forEach(setInDragLayer); + // if rows or columns were set in options, use those values + if (oRows != null && oCols != null) { + rows = oRows; + cols = oCols; + } else if (oRows != null && oCols == null) { + rows = oRows; + cols = Math.ceil(cells / rows); + } else if (oRows == null && oCols != null) { + cols = oCols; + rows = Math.ceil(cells / cols); } - if (opts.addToList) { - nodes.forEach(function (ele) { - addToDragList(ele, opts); - }); - } - }; + // otherwise use the automatic values and adjust accordingly - var haveMutationsApi = typeof MutationObserver !== 'undefined'; + // if rounding was up, see if we can reduce rows or columns + else if (cols * rows > cells) { + var sm = small(); + var lg = large(); - // watch for when the cy container is removed from the dom - if (haveMutationsApi) { - r.removeObserver = new MutationObserver(function (mutns) { - // eslint-disable-line no-undef - for (var i = 0; i < mutns.length; i++) { - var mutn = mutns[i]; - var rNodes = mutn.removedNodes; + // reducing the small side takes away the most cells, so try it first + if ((sm - 1) * lg >= cells) { + small(sm - 1); + } else if ((lg - 1) * sm >= cells) { + large(lg - 1); + } + } else { - if (rNodes) { - for (var j = 0; j < rNodes.length; j++) { - var rNode = rNodes[j]; + // if rounding was too low, add rows or columns + while (cols * rows < cells) { + var _sm = small(); + var _lg = large(); - if (rNode === r.container) { - r.destroy(); - break; - } + // try to add to larger side first (adds less in multiplication) + if ((_lg + 1) * _sm >= cells) { + large(_lg + 1); + } else { + small(_sm + 1); } } } - }); - if (r.container.parentNode) { - r.removeObserver.observe(r.container.parentNode, { childList: true }); - } - } else { - r.registerBinding(r.container, 'DOMNodeRemoved', function (e) { - r.destroy(); - }); - } + var cellWidth = bb.w / cols; + var cellHeight = bb.h / rows; - var onResize = util.debounce(function () { - r.cy.resize(); - }, 100); + if (options.condense) { + cellWidth = 0; + cellHeight = 0; + } - if (haveMutationsApi) { - r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef + if (options.avoidOverlap) { + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var pos = node._private.position; - r.styleObserver.observe(r.container, { attributes: true }); - } + if (pos.x == null || pos.y == null) { + // for bb + pos.x = 0; + pos.y = 0; + } - // auto resize - r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef + var nbb = node.layoutDimensions(options); + var p = options.avoidOverlapPadding; - var forEachUp = function forEachUp(domEle, fn) { - while (domEle != null) { - fn(domEle); + var w = nbb.w + p; + var h = nbb.h + p; - domEle = domEle.parentNode; + cellWidth = Math.max(cellWidth, w); + cellHeight = Math.max(cellHeight, h); + } } - }; - var invalidateCoords = function invalidateCoords() { - r.invalidateContainerClientCoordsCache(); - }; - - forEachUp(r.container, function (domEle) { - r.registerBinding(domEle, 'transitionend', invalidateCoords); - r.registerBinding(domEle, 'animationend', invalidateCoords); - r.registerBinding(domEle, 'scroll', invalidateCoords); - }); + var cellUsed = {}; // e.g. 'c-0-2' => true - // stop right click menu from appearing on cy - r.registerBinding(r.container, 'contextmenu', function (e) { - e.preventDefault(); - }); + var used = function used(row, col) { + return cellUsed['c-' + row + '-' + col] ? true : false; + }; - var inBoxSelection = function inBoxSelection() { - return r.selection[4] !== 0; - }; + var use = function use(row, col) { + cellUsed['c-' + row + '-' + col] = true; + }; - var eventInContainer = function eventInContainer(e) { - // save cycles if mouse events aren't to be captured - var containerPageCoords = r.findContainerClientCoords(); - var x = containerPageCoords[0]; - var y = containerPageCoords[1]; - var width = containerPageCoords[2]; - var height = containerPageCoords[3]; + // to keep track of current cell position + var row = 0; + var col = 0; + var moveToNextCell = function moveToNextCell() { + col++; + if (col >= cols) { + col = 0; + row++; + } + }; - var positions = e.touches ? e.touches : [e]; - var atLeastOnePosInside = false; + // get a cache of all the manual positions + var id2manPos = {}; + for (var _i = 0; _i < nodes.length; _i++) { + var _node = nodes[_i]; + var rcPos = options.position(_node); - for (var i = 0; i < positions.length; i++) { - var p = positions[i]; + if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) { + // must have at least row or col def'd + var _pos = { + row: rcPos.row, + col: rcPos.col + }; - if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) { - atLeastOnePosInside = true; - break; - } - } + if (_pos.col === undefined) { + // find unused col + _pos.col = 0; - if (!atLeastOnePosInside) { - return false; - } + while (used(_pos.row, _pos.col)) { + _pos.col++; + } + } else if (_pos.row === undefined) { + // find unused row + _pos.row = 0; - var container = r.container; - var target = e.target; - var tParent = target.parentNode; - var containerIsTarget = false; + while (used(_pos.row, _pos.col)) { + _pos.row++; + } + } - while (tParent) { - if (tParent === container) { - containerIsTarget = true; - break; + id2manPos[_node.id()] = _pos; + use(_pos.row, _pos.col); } - - tParent = tParent.parentNode; } - if (!containerIsTarget) { - return false; - } // if target is outisde cy container, then this event is not for us + var getPos = function getPos(element, i) { + var x = void 0, + y = void 0; - return true; - }; + if (element.locked() || element.isParent()) { + return false; + } - // Primary key - r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) { - if (!eventInContainer(e)) { - return; - } + // see if we have a manual position set + var rcPos = id2manPos[element.id()]; + if (rcPos) { + x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1; + y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1; + } else { + // otherwise set automatically + + while (used(row, col)) { + moveToNextCell(); + } - e.preventDefault(); - r.hoverData.capture = true; - r.hoverData.which = e.which; + x = col * cellWidth + cellWidth / 2 + bb.x1; + y = row * cellHeight + cellHeight / 2 + bb.y1; + use(row, col); - var cy = r.cy; - var gpos = [e.clientX, e.clientY]; - var pos = r.projectIntoViewport(gpos[0], gpos[1]); - var select = r.selection; - var nears = r.findNearestElements(pos[0], pos[1], true, false); - var near = nears[0]; - var draggedElements = r.dragData.possibleDragElements; + moveToNextCell(); + } - r.hoverData.mdownPos = pos; - r.hoverData.mdownGPos = gpos; + return { x: x, y: y }; + }; - var checkForTaphold = function checkForTaphold() { - r.hoverData.tapholdCancelled = false; + nodes.layoutPositions(this, options, getPos); + } - clearTimeout(r.hoverData.tapholdTimeout); + return this; // chaining +}; - r.hoverData.tapholdTimeout = setTimeout(function () { +module.exports = GridLayout; - if (r.hoverData.tapholdCancelled) { - return; - } else { - var ele = r.hoverData.down; +/***/ }), +/* 102 */ +/***/ (function(module, exports, __webpack_require__) { - if (ele) { - ele.emit({ - originalEvent: e, - type: 'taphold', - position: { x: pos[0], y: pos[1] } - }); - } else { - cy.emit({ - originalEvent: e, - type: 'taphold', - position: { x: pos[0], y: pos[1] } - }); - } - } - }, r.tapholdDuration); - }; +"use strict"; - // Right click button - if (e.which == 3) { - r.hoverData.cxtStarted = true; +var util = __webpack_require__(1); - var cxtEvt = { - originalEvent: e, - type: 'cxttapstart', - position: { x: pos[0], y: pos[1] } - }; +// default layout options +var defaults = { + ready: function ready() {}, // on layoutready + stop: function stop() {} // on layoutstop +}; - if (near) { - near.activate(); - near.emit(cxtEvt); +// constructor +// options : object containing layout options +function NullLayout(options) { + this.options = util.extend({}, defaults, options); +} - r.hoverData.down = near; - } else { - cy.emit(cxtEvt); - } +// runs the layout +NullLayout.prototype.run = function () { + var options = this.options; + var eles = options.eles; // elements to consider in the layout + var layout = this; - r.hoverData.downTime = new Date().getTime(); - r.hoverData.cxtDragged = false; + // cy is automatically populated for us in the constructor + var cy = options.cy; // jshint ignore:line - // Primary button - } else if (e.which == 1) { + layout.emit('layoutstart'); - if (near) { - near.activate(); - } + // puts all nodes at (0, 0) + eles.nodes().positions(function () { + return { + x: 0, + y: 0 + }; + }); - // Element dragging - { - // If something is under the cursor and it is draggable, prepare to grab it - if (near != null) { + // trigger layoutready when each node has had its position set at least once + layout.one('layoutready', options.ready); + layout.emit('layoutready'); - if (r.nodeIsGrabbable(near)) { + // trigger layoutstop when the layout stops (e.g. finishes) + layout.one('layoutstop', options.stop); + layout.emit('layoutstop'); - var makeEvent = function makeEvent(type) { - return { - originalEvent: e, - type: type, - position: { x: pos[0], y: pos[1] } - }; - }; + return this; // chaining +}; - var triggerGrab = function triggerGrab(ele) { - ele.emit(makeEvent('grab')); - }; +// called on continuous layouts to stop them before they finish +NullLayout.prototype.stop = function () { + return this; // chaining +}; - setGrabTarget(near); +module.exports = NullLayout; - if (!near.selected()) { +/***/ }), +/* 103 */ +/***/ (function(module, exports, __webpack_require__) { - draggedElements = r.dragData.possibleDragElements = []; - addNodeToDrag(near, { addToList: draggedElements }); +"use strict"; - near.emit(makeEvent('grabon')).emit(makeEvent('grab')); - } else { - draggedElements = r.dragData.possibleDragElements = []; - var selectedNodes = cy.$(function (ele) { - return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele); - }); +var util = __webpack_require__(1); +var is = __webpack_require__(0); - addNodesToDrag(selectedNodes, { addToList: draggedElements }); +var defaults = { + positions: undefined, // map of (node id) => (position obj); or function(node){ return somPos; } + zoom: undefined, // the zoom level to set (prob want fit = false if set) + pan: undefined, // the pan level to set (prob want fit = false if set) + fit: true, // whether to fit to viewport + padding: 30, // padding on fit + animate: false, // whether to transition the node positions + animationDuration: 500, // duration of animation in ms if enabled + animationEasing: undefined, // easing of animation if enabled + animateFilter: function animateFilter(node, i) { + return true; + }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts + ready: undefined, // callback on layoutready + stop: undefined, // callback on layoutstop + transform: function transform(node, position) { + return position; + } // transform a given node position. Useful for changing flow direction in discrete layouts +}; - near.emit(makeEvent('grabon')); +function PresetLayout(options) { + this.options = util.extend({}, defaults, options); +} - selectedNodes.forEach(triggerGrab); - } +PresetLayout.prototype.run = function () { + var options = this.options; + var eles = options.eles; - r.redrawHint('eles', true); - r.redrawHint('drag', true); - } - } + var nodes = eles.nodes(); + var posIsFn = is.fn(options.positions); - r.hoverData.down = near; - r.hoverData.downs = nears; - r.hoverData.downTime = new Date().getTime(); - } + function getPosition(node) { + if (options.positions == null) { + return null; + } - triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, { - position: { x: pos[0], y: pos[1] } - }); + if (posIsFn) { + return options.positions(node); + } - if (near == null) { - select[4] = 1; + var pos = options.positions[node._private.data.id]; - r.data.bgActivePosistion = { - x: pos[0], - y: pos[1] - }; + if (pos == null) { + return null; + } - r.redrawHint('select', true); + return pos; + } - r.redraw(); - } else if (near.isEdge()) { - select[4] = 1; // for future pan - } + nodes.layoutPositions(this, options, function (node, i) { + var position = getPosition(node); - checkForTaphold(); + if (node.locked() || position == null) { + return false; } - // Initialize selection box coordinates - select[0] = select[2] = pos[0]; - select[1] = select[3] = pos[1]; - }, false); + return position; + }); - r.registerBinding(window, 'mousemove', function mousemoveHandler(e) { - // eslint-disable-line no-undef - var capture = r.hoverData.capture; + return this; // chaining +}; - if (!capture && !eventInContainer(e)) { - return; - } +module.exports = PresetLayout; - var preventDefault = false; - var cy = r.cy; - var zoom = cy.zoom(); - var gpos = [e.clientX, e.clientY]; - var pos = r.projectIntoViewport(gpos[0], gpos[1]); - var mdownPos = r.hoverData.mdownPos; - var mdownGPos = r.hoverData.mdownGPos; - var select = r.selection; +/***/ }), +/* 104 */ +/***/ (function(module, exports, __webpack_require__) { - var near = null; - if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) { - near = r.findNearestElement(pos[0], pos[1], true, false); - } - var last = r.hoverData.last; - var down = r.hoverData.down; +"use strict"; - var disp = [pos[0] - select[2], pos[1] - select[3]]; - var draggedElements = r.dragData.possibleDragElements; +var util = __webpack_require__(1); +var math = __webpack_require__(2); + +var defaults = { + fit: true, // whether to fit to viewport + padding: 30, // fit padding + boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } + animate: false, // whether to transition the node positions + animationDuration: 500, // duration of animation in ms if enabled + animationEasing: undefined, // easing of animation if enabled + animateFilter: function animateFilter(node, i) { + return true; + }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts + ready: undefined, // callback on layoutready + stop: undefined, // callback on layoutstop + transform: function transform(node, position) { + return position; + } // transform a given node position. Useful for changing flow direction in discrete layouts +}; + +function RandomLayout(options) { + this.options = util.extend({}, defaults, options); +} - var isOverThresholdDrag; +RandomLayout.prototype.run = function () { + var options = this.options; + var cy = options.cy; + var eles = options.eles; + var nodes = eles.nodes().not(':parent'); - if (mdownGPos) { - var dx = gpos[0] - mdownGPos[0]; - var dx2 = dx * dx; - var dy = gpos[1] - mdownGPos[1]; - var dy2 = dy * dy; - var dist2 = dx2 + dy2; + var bb = math.makeBoundingBox(options.boundingBox ? options.boundingBox : { + x1: 0, y1: 0, w: cy.width(), h: cy.height() + }); - r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2; - } + var getPos = function getPos(node, i) { + return { + x: bb.x1 + Math.round(Math.random() * bb.w), + y: bb.y1 + Math.round(Math.random() * bb.h) + }; + }; - var multSelKeyDown = isMultSelKeyDown(e); + nodes.layoutPositions(this, options, getPos); - if (isOverThresholdDrag) { - r.hoverData.tapholdCancelled = true; - } + return this; // chaining +}; - var updateDragDelta = function updateDragDelta() { - var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || []; +module.exports = RandomLayout; - if (dragDelta.length === 0) { - dragDelta.push(disp[0]); - dragDelta.push(disp[1]); - } else { - dragDelta[0] += disp[0]; - dragDelta[1] += disp[1]; - } - }; +/***/ }), +/* 105 */ +/***/ (function(module, exports, __webpack_require__) { - preventDefault = true; +"use strict"; - triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, { - position: { x: pos[0], y: pos[1] } - }); - var goIntoBoxMode = function goIntoBoxMode() { - r.data.bgActivePosistion = undefined; +module.exports = [{ name: 'null', impl: __webpack_require__(106) }, { name: 'base', impl: __webpack_require__(107) }, { name: 'canvas', impl: __webpack_require__(123) }]; - if (!r.hoverData.selecting) { - cy.emit('boxstart'); - } +/***/ }), +/* 106 */ +/***/ (function(module, exports, __webpack_require__) { - select[4] = 1; - r.hoverData.selecting = true; +"use strict"; - r.redrawHint('select', true); - r.redraw(); - }; - // trigger context drag if rmouse down - if (r.hoverData.which === 3) { - // but only if over threshold - if (isOverThresholdDrag) { - var cxtEvt = { - originalEvent: e, - type: 'cxtdrag', - position: { x: pos[0], y: pos[1] } - }; +function NullRenderer(options) { + this.options = options; + this.notifications = 0; // for testing +} - if (down) { - down.emit(cxtEvt); - } else { - cy.emit(cxtEvt); - } +var noop = function noop() {}; - r.hoverData.cxtDragged = true; +NullRenderer.prototype = { + recalculateRenderedStyle: noop, + notify: function notify() { + this.notifications++; + }, + init: noop +}; - if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) { +module.exports = NullRenderer; - if (r.hoverData.cxtOver) { - r.hoverData.cxtOver.emit({ - originalEvent: e, - type: 'cxtdragout', - position: { x: pos[0], y: pos[1] } - }); - } +/***/ }), +/* 107 */ +/***/ (function(module, exports, __webpack_require__) { - r.hoverData.cxtOver = near; +"use strict"; - if (near) { - near.emit({ - originalEvent: e, - type: 'cxtdragover', - position: { x: pos[0], y: pos[1] } - }); - } - } - } - // Check if we are drag panning the entire graph - } else if (r.hoverData.dragging) { - preventDefault = true; +var is = __webpack_require__(0); +var util = __webpack_require__(1); +var window = __webpack_require__(3); - if (cy.panningEnabled() && cy.userPanningEnabled()) { - var deltaP; +var BaseRenderer = function BaseRenderer(options) { + this.init(options); +}; +var BR = BaseRenderer; +var BRp = BR.prototype; - if (r.hoverData.justStartedPan) { - var mdPos = r.hoverData.mdownPos; +BRp.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl']; - deltaP = { - x: (pos[0] - mdPos[0]) * zoom, - y: (pos[1] - mdPos[1]) * zoom - }; +BRp.init = function (options) { + var r = this; - r.hoverData.justStartedPan = false; - } else { - deltaP = { - x: disp[0] * zoom, - y: disp[1] * zoom - }; - } + r.options = options; - cy.panBy(deltaP); + r.cy = options.cy; - r.hoverData.dragged = true; - } + var ctr = r.container = options.cy.container(); - // Needs reproject due to pan changing viewport - pos = r.projectIntoViewport(e.clientX, e.clientY); + // prepend a stylesheet in the head such that + if (window) { + var document = window.document; + var head = document.head; + var stylesheetId = '__________cytoscape_stylesheet'; + var className = '__________cytoscape_container'; + var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null; - // Checks primary button down & out of time & mouse not moved much - } else if (select[4] == 1 && (down == null || down.isEdge())) { + if (ctr.className.indexOf(className) < 0) { + ctr.className = (ctr.className || '') + ' ' + className; + } - if (isOverThresholdDrag) { + if (!stylesheetAlreadyExists) { + var stylesheet = document.createElement('style'); - if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) { - goIntoBoxMode(); - } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) { - var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs); + stylesheet.id = stylesheetId; + stylesheet.innerHTML = '.' + className + ' { position: relative; }'; - if (allowPassthrough) { - r.hoverData.dragging = true; - r.hoverData.justStartedPan = true; - select[4] = 0; + head.insertBefore(stylesheet, head.children[0]); // first so lowest priority + } - r.data.bgActivePosistion = math.array2point(mdownPos); + var computedStyle = window.getComputedStyle(ctr); + var position = computedStyle.getPropertyValue('position'); - r.redrawHint('select', true); - r.redraw(); - } - } + if (position === 'static') { + util.error('A Cytoscape container has style position:static and so can not use UI extensions properly'); + } + } - if (down && down.isEdge() && down.active()) { - down.unactivate(); - } - } - } else { - if (down && down.isEdge() && down.active()) { - down.unactivate(); - } + r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag - if ((!down || !down.grabbed()) && near != last) { + r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; - if (last) { - triggerEvents(last, ['mouseout', 'tapdragout'], e, { - position: { x: pos[0], y: pos[1] } - }); - } + //--Pointer-related data + r.hoverData = { down: null, last: null, + downTime: null, triggerMode: null, + dragging: false, + initialPan: [null, null], capture: false }; - if (near) { - triggerEvents(near, ['mouseover', 'tapdragover'], e, { - position: { x: pos[0], y: pos[1] } - }); - } + r.dragData = { possibleDragElements: [] }; - r.hoverData.last = near; - } + r.touchData = { + start: null, capture: false, - if (down) { + // These 3 fields related to tap, taphold events + startPosition: [null, null, null, null, null, null], + singleTouchStartTime: null, + singleTouchMoved: true, - if (isOverThresholdDrag) { - // then we can take action + now: [null, null, null, null, null, null], + earlier: [null, null, null, null, null, null] + }; - if (cy.boxSelectionEnabled() && multSelKeyDown) { - // then selection overrides - if (down && down.grabbed()) { - freeDraggedElements(draggedElements); + r.redraws = 0; + r.showFps = options.showFps; + r.debug = options.debug; - down.emit('free'); - } + r.hideEdgesOnViewport = options.hideEdgesOnViewport; + r.hideLabelsOnViewport = options.hideLabelsOnViewport; + r.textureOnViewport = options.textureOnViewport; + r.wheelSensitivity = options.wheelSensitivity; + r.motionBlurEnabled = options.motionBlur; // on by default + r.forcedPixelRatio = options.pixelRatio; + r.motionBlur = options.motionBlur; // for initial kick off + r.motionBlurOpacity = options.motionBlurOpacity; + r.motionBlurTransparency = 1 - r.motionBlurOpacity; + r.motionBlurPxRatio = 1; + r.mbPxRBlurry = 1; //0.8; + r.minMbLowQualFrames = 4; + r.fullQualityMb = false; + r.clearedForMotionBlur = []; + r.desktopTapThreshold = options.desktopTapThreshold; + r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold; + r.touchTapThreshold = options.touchTapThreshold; + r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold; + r.tapholdDuration = 500; - goIntoBoxMode(); - } else if (down && down.grabbed() && r.nodeIsDraggable(down)) { - // drag node - var justStartedDrag = !r.dragData.didDrag; + r.bindings = []; + r.beforeRenderCallbacks = []; + r.beforeRenderPriorities = { // higher priority execs before lower one + animations: 400, + eleCalcs: 300, + eleTxrDeq: 200, + lyrTxrDeq: 100 + }; - if (justStartedDrag) { - r.redrawHint('eles', true); - } + r.registerNodeShapes(); + r.registerArrowShapes(); + r.registerCalculationListeners(); +}; - r.dragData.didDrag = true; // indicate that we actually did drag the node +BRp.notify = function (params) { + var types; + var r = this; - var toTrigger = []; + // the renderer can't be notified after it's destroyed + if (this.destroyed) { + return; + } - // now, add the elements to the drag layer if not done already - if (!r.hoverData.draggingEles) { - addNodesToDrag(cy.collection(draggedElements), { inDragLayer: true }); - } + if (is.array(params.type)) { + types = params.type; + } else { + types = [params.type]; + } - for (var i = 0; i < draggedElements.length; i++) { - var dEle = draggedElements[i]; + var has = {}; + for (var i = 0; i < types.length; i++) { + var type = types[i]; - // Locked nodes not draggable, as well as non-visible nodes - if (r.nodeIsDraggable(dEle) && dEle.grabbed()) { - var dPos = dEle.position(); + has[type] = true; + } // for - toTrigger.push(dEle); + if (has['init']) { + r.load(); + return; + } - if (is.number(disp[0]) && is.number(disp[1])) { - dPos.x += disp[0]; - dPos.y += disp[1]; + if (has['destroy']) { + r.destroy(); + return; + } - if (justStartedDrag) { - var dragDelta = r.hoverData.dragDelta; + if (has['add'] || has['remove'] || has['load'] || has['zorder']) { + r.invalidateCachedZSortedEles(); + } - if (dragDelta && is.number(dragDelta[0]) && is.number(dragDelta[1])) { - dPos.x += dragDelta[0]; - dPos.y += dragDelta[1]; - } - } - } - } - } + if (has['viewport']) { + r.redrawHint('select', true); + } - r.hoverData.draggingEles = true; + if (has['load'] || has['resize']) { + r.invalidateContainerClientCoordsCache(); + r.matchCanvasSize(r.container); + } - var tcol = cy.collection(toTrigger); + r.redrawHint('eles', true); + r.redrawHint('drag', true); - tcol.dirtyCompoundBoundsCache(); - tcol.emit('position drag'); + this.startRenderLoop(); - r.redrawHint('drag', true); - r.redraw(); - } - } else { - // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant - updateDragDelta(); - } - } + this.redraw(); +}; - // prevent the dragging from triggering text selection on the page - preventDefault = true; - } +BRp.destroy = function () { + var r = this; - select[2] = pos[0];select[3] = pos[1]; + r.destroyed = true; - if (preventDefault) { - if (e.stopPropagation) e.stopPropagation(); - if (e.preventDefault) e.preventDefault(); - return false; - } - }, false); + r.cy.stopAnimationLoop(); - r.registerBinding(window, 'mouseup', function mouseupHandler(e) { - // eslint-disable-line no-undef - var capture = r.hoverData.capture; - if (!capture) { - return; - } - r.hoverData.capture = false; + for (var i = 0; i < r.bindings.length; i++) { + var binding = r.bindings[i]; + var b = binding; + var tgt = b.target; - var cy = r.cy;var pos = r.projectIntoViewport(e.clientX, e.clientY);var select = r.selection; - var near = r.findNearestElement(pos[0], pos[1], true, false); - var draggedElements = r.dragData.possibleDragElements;var down = r.hoverData.down; - var multSelKeyDown = isMultSelKeyDown(e); + (tgt.off || tgt.removeEventListener).apply(tgt, b.args); + } - if (r.data.bgActivePosistion) { - r.redrawHint('select', true); - r.redraw(); - } + r.bindings = []; + r.beforeRenderCallbacks = []; + r.onUpdateEleCalcsFns = []; - r.hoverData.tapholdCancelled = true; + if (r.removeObserver) { + r.removeObserver.disconnect(); + } - r.data.bgActivePosistion = undefined; // not active bg now + if (r.styleObserver) { + r.styleObserver.disconnect(); + } - if (down) { - down.unactivate(); + if (r.labelCalcDiv) { + try { + document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef + } catch (e) { + // ie10 issue #1014 } + } +}; - if (r.hoverData.which === 3) { - var cxtEvt = { - originalEvent: e, - type: 'cxttapend', - position: { x: pos[0], y: pos[1] } - }; +[__webpack_require__(108), __webpack_require__(109), __webpack_require__(119), __webpack_require__(120), __webpack_require__(121), __webpack_require__(122)].forEach(function (props) { + util.extend(BRp, props); +}); - if (down) { - down.emit(cxtEvt); - } else { - cy.emit(cxtEvt); - } +module.exports = BR; - if (!r.hoverData.cxtDragged) { - var cxtTap = { - originalEvent: e, - type: 'cxttap', - position: { x: pos[0], y: pos[1] } - }; +/***/ }), +/* 108 */ +/***/ (function(module, exports, __webpack_require__) { - if (down) { - down.emit(cxtTap); - } else { - cy.emit(cxtTap); - } - } +"use strict"; - r.hoverData.cxtDragged = false; - r.hoverData.which = null; - } else if (r.hoverData.which === 1) { - // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something - if (down == null && // not mousedown on node - !r.dragData.didDrag // didn't move the node around - && !r.hoverData.selecting // not box selection - && !r.hoverData.dragged // didn't pan - && !isMultSelKeyDown(e)) { +var math = __webpack_require__(2); +var is = __webpack_require__(0); +var util = __webpack_require__(1); - cy.$(function (ele) { - return ele.selected(); - }).unselect(); +var BRp = {}; - if (draggedElements.length > 0) { - r.redrawHint('eles', true); - } +BRp.arrowShapeWidth = 0.3; - r.dragData.possibleDragElements = draggedElements = []; - } +BRp.registerArrowShapes = function () { + var arrowShapes = this.arrowShapes = {}; + var renderer = this; - triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, { - position: { x: pos[0], y: pos[1] } - }); + // Contract for arrow shapes: + // 0, 0 is arrow tip + // (0, 1) is direction towards node + // (1, 0) is right + // + // functional api: + // collide: check x, y in shape + // roughCollide: called before collide, no false negatives + // draw: draw + // spacing: dist(arrowTip, nodeBoundary) + // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip - if (!r.dragData.didDrag // didn't move a node around - && !r.hoverData.dragged // didn't pan - && !r.hoverData.selecting // not box selection - && !r.hoverData.isOverThresholdDrag // didn't move too much - ) { - triggerEvents(down, ['click', 'tap', 'vclick'], e, { - position: { x: pos[0], y: pos[1] } - }); - } + var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) { + var x1 = translation.x - size / 2 - padding; + var x2 = translation.x + size / 2 + padding; + var y1 = translation.y - size / 2 - padding; + var y2 = translation.y + size / 2 + padding; - // Single selection - if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) { - if (near != null && near._private.selectable) { + var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2; - if (r.hoverData.dragging) { - // if panning, don't change selection state - } else if (cy.selectionType() === 'additive' || multSelKeyDown) { - if (near.selected()) { - near.unselect(); - } else { - near.select(); - } - } else { - if (!multSelKeyDown) { - cy.$(':selected').unmerge(near).unselect(); - near.select(); - } - } + return inside; + }; - r.redrawHint('eles', true); - } - } + var transform = function transform(x, y, size, angle, translation) { + var xRotated = x * Math.cos(angle) - y * Math.sin(angle); + var yRotated = x * Math.sin(angle) + y * Math.cos(angle); - if (r.hoverData.selecting) { - var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3])); + var xScaled = xRotated * size; + var yScaled = yRotated * size; - r.redrawHint('select', true); + var xTranslated = xScaled + translation.x; + var yTranslated = yScaled + translation.y; - if (box.length > 0) { - r.redrawHint('eles', true); - } + return { + x: xTranslated, + y: yTranslated + }; + }; - cy.emit('boxend'); + var transformPoints = function transformPoints(pts, size, angle, translation) { + var retPts = []; - var eleWouldBeSelected = function eleWouldBeSelected(ele) { - return ele.selectable() && !ele.selected(); - }; + for (var i = 0; i < pts.length; i += 2) { + var x = pts[i]; + var y = pts[i + 1]; - if (cy.selectionType() === 'additive') { - box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect'); - } else { - if (!multSelKeyDown) { - cy.$(':selected').unmerge(box).unselect(); - } + retPts.push(transform(x, y, size, angle, translation)); + } - box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect'); - } + return retPts; + }; - // always need redraw in case eles unselectable - r.redraw(); - } + var pointsToArr = function pointsToArr(pts) { + var ret = []; - // Cancel drag pan - if (r.hoverData.dragging) { - r.hoverData.dragging = false; + for (var i = 0; i < pts.length; i++) { + var p = pts[i]; - r.redrawHint('select', true); - r.redrawHint('eles', true); + ret.push(p.x, p.y); + } - r.redraw(); - } + return ret; + }; - if (!select[4]) { - r.redrawHint('drag', true); - r.redrawHint('eles', true); + var standardGap = function standardGap(edge) { + return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2; + }; - var downWasGrabbed = down && down.grabbed(); + var defineArrowShape = function defineArrowShape(name, defn) { + if (is.string(defn)) { + defn = arrowShapes[defn]; + } - freeDraggedElements(draggedElements); + arrowShapes[name] = util.extend({ + name: name, - if (downWasGrabbed) { - down.emit('free'); - } - } - } // else not right mouse + points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3], - select[4] = 0;r.hoverData.down = null; + collide: function collide(x, y, size, angle, translation, padding) { + var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation)); + var inside = math.pointInsidePolygonPoints(x, y, points); - r.hoverData.cxtStarted = false; - r.hoverData.draggingEles = false; - r.hoverData.selecting = false; - r.hoverData.isOverThresholdDrag = false; - r.dragData.didDrag = false; - r.hoverData.dragged = false; - r.hoverData.dragDelta = []; - r.hoverData.mdownPos = null; - r.hoverData.mdownGPos = null; - }, false); + return inside; + }, - var wheelHandler = function wheelHandler(e) { + roughCollide: bbCollide, - if (r.scrollingPage) { - return; - } // while scrolling, ignore wheel-to-zoom + draw: function draw(context, size, angle, translation) { + var points = transformPoints(this.points, size, angle, translation); - var cy = r.cy; - var pos = r.projectIntoViewport(e.clientX, e.clientY); - var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y]; + renderer.arrowShapeImpl('polygon')(context, points); + }, - if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) { - // if pan dragging or cxt dragging, wheel movements make no zoom - e.preventDefault(); - return; - } + spacing: function spacing(edge) { + return 0; + }, - if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) { - e.preventDefault(); + gap: standardGap + }, defn); + }; - r.data.wheelZooming = true; - clearTimeout(r.data.wheelTimeout); - r.data.wheelTimeout = setTimeout(function () { - r.data.wheelZooming = false; + defineArrowShape('none', { + collide: util.falsify, - r.redrawHint('eles', true); - r.redraw(); - }, 150); + roughCollide: util.falsify, - var diff; + draw: util.noop, - if (e.deltaY != null) { - diff = e.deltaY / -250; - } else if (e.wheelDeltaY != null) { - diff = e.wheelDeltaY / 1000; - } else { - diff = e.wheelDelta / 1000; - } + spacing: util.zeroify, - diff = diff * r.wheelSensitivity; + gap: util.zeroify + }); - var needsWheelFix = e.deltaMode === 1; - if (needsWheelFix) { - // fixes slow wheel events on ff/linux and ff/windows - diff *= 33; - } + defineArrowShape('triangle', { + points: [-0.15, -0.3, 0, 0, 0.15, -0.3] + }); - cy.zoom({ - level: cy.zoom() * Math.pow(10, diff), - renderedPosition: { x: rpos[0], y: rpos[1] } - }); - } - }; + defineArrowShape('arrow', 'triangle'); - // Functions to help with whether mouse wheel should trigger zooming - // -- - r.registerBinding(r.container, 'wheel', wheelHandler, true); + defineArrowShape('triangle-backcurve', { + points: arrowShapes['triangle'].points, - // disable nonstandard wheel events - // r.registerBinding(r.container, 'mousewheel', wheelHandler, true); - // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true); - // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox + controlPoint: [0, -0.15], - r.registerBinding(window, 'scroll', function scrollHandler(e) { - // eslint-disable-line no-undef - r.scrollingPage = true; + roughCollide: bbCollide, - clearTimeout(r.scrollingPageTimeout); - r.scrollingPageTimeout = setTimeout(function () { - r.scrollingPage = false; - }, 250); - }, true); + draw: function draw(context, size, angle, translation, edgeWidth) { + var ptsTrans = transformPoints(this.points, size, angle, translation); + var ctrlPt = this.controlPoint; + var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation); - // Functions to help with handling mouseout/mouseover on the Cytoscape container - // Handle mouseout on Cytoscape container - r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) { - var pos = r.projectIntoViewport(e.clientX, e.clientY); + renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans); + }, - r.cy.emit({ - originalEvent: e, - type: 'mouseout', - position: { x: pos[0], y: pos[1] } - }); - }, false); + gap: function gap(edge) { + return standardGap(edge) * 0.8; + } + }); - r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) { - var pos = r.projectIntoViewport(e.clientX, e.clientY); + defineArrowShape('triangle-tee', { + points: [-0.15, -0.3, 0, 0, 0.15, -0.3, -0.15, -0.3], - r.cy.emit({ - originalEvent: e, - type: 'mouseover', - position: { x: pos[0], y: pos[1] } - }); - }, false); + pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4], - var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom - var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom - var center1, modelCenter1; // center point on start pinch to zoom - var offsetLeft, offsetTop; - var containerWidth, containerHeight; - var twoFingersStartInside; + collide: function collide(x, y, size, angle, translation, edgeWidth, padding) { + var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation)); + var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation)); - var distance = function distance(x1, y1, x2, y2) { - return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - }; + var inside = math.pointInsidePolygonPoints(x, y, triPts) || math.pointInsidePolygonPoints(x, y, teePts); - var distanceSq = function distanceSq(x1, y1, x2, y2) { - return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); - }; + return inside; + }, - var touchstartHandler; - r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) { - if (!eventInContainer(e)) { - return; + draw: function draw(context, size, angle, translation, edgeWidth) { + var triPts = transformPoints(this.points, size, angle, translation); + var teePts = transformPoints(this.pointsTee, size, angle, translation); + + renderer.arrowShapeImpl(this.name)(context, triPts, teePts); } + }); - r.touchData.capture = true; - r.data.bgActivePosistion = undefined; + defineArrowShape('triangle-cross', { + points: [-0.15, -0.3, 0, 0, 0.15, -0.3, -0.15, -0.3], - var cy = r.cy; - var now = r.touchData.now; - var earlier = r.touchData.earlier; + baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle + -0.15, -0.4, 0.15, -0.4, // second half of the rectangle + 0.15, -0.4], - if (e.touches[0]) { - var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);now[0] = pos[0];now[1] = pos[1]; - } - if (e.touches[1]) { - var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);now[2] = pos[0];now[3] = pos[1]; - } - if (e.touches[2]) { - var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);now[4] = pos[0];now[5] = pos[1]; - } + crossLinePts: function crossLinePts(size, edgeWidth) { + // shift points so that the distance between the cross points matches edge width + var p = this.baseCrossLinePts.slice(); + var shiftFactor = edgeWidth / size; + var y0 = 3; + var y1 = 5; - // record starting points for pinch-to-zoom - if (e.touches[1]) { + p[y0] = p[y0] - shiftFactor; + p[y1] = p[y1] - shiftFactor; - freeDraggedElements(r.dragData.touchDragEles); + return p; + }, - var offsets = r.findContainerClientCoords(); - offsetLeft = offsets[0]; - offsetTop = offsets[1]; - containerWidth = offsets[2]; - containerHeight = offsets[3]; + collide: function collide(x, y, size, angle, translation, edgeWidth, padding) { + var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation)); + var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation)); + var inside = math.pointInsidePolygonPoints(x, y, triPts) || math.pointInsidePolygonPoints(x, y, teePts); - f1x1 = e.touches[0].clientX - offsetLeft; - f1y1 = e.touches[0].clientY - offsetTop; + return inside; + }, - f2x1 = e.touches[1].clientX - offsetLeft; - f2y1 = e.touches[1].clientY - offsetTop; + draw: function draw(context, size, angle, translation, edgeWidth) { + var triPts = transformPoints(this.points, size, angle, translation); + var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation); - twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight; + renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts); + } + }); - var pan = cy.pan(); - var zoom = cy.zoom(); + defineArrowShape('vee', { + points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15], - distance1 = distance(f1x1, f1y1, f2x1, f2y1); - distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1); - center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2]; - modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; + gap: function gap(edge) { + return standardGap(edge) * 0.525; + } + }); - // consider context tap - var cxtDistThreshold = 200; - var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold; - if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) { + defineArrowShape('circle', { + radius: 0.15, - var near1 = r.findNearestElement(now[0], now[1], true, true); - var near2 = r.findNearestElement(now[2], now[3], true, true); + collide: function collide(x, y, size, angle, translation, edgeWidth, padding) { + var t = translation; + var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2); - if (near1 && near1.isNode()) { - near1.activate().emit({ - originalEvent: e, - type: 'cxttapstart', - position: { x: now[0], y: now[1] } - }); - r.touchData.start = near1; - } else if (near2 && near2.isNode()) { - near2.activate().emit({ - originalEvent: e, - type: 'cxttapstart', - position: { x: now[0], y: now[1] } - }); - r.touchData.start = near2; - } else { - cy.emit({ - originalEvent: e, - type: 'cxttapstart', - position: { x: now[0], y: now[1] } - }); - } + return inside; + }, - if (r.touchData.start) { - r.touchData.start._private.grabbed = false; - } - r.touchData.cxt = true; - r.touchData.cxtDragged = false; - r.data.bgActivePosistion = undefined; + draw: function draw(context, size, angle, translation, edgeWidth) { + renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size); + }, - r.redraw(); - return; - } + spacing: function spacing(edge) { + return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius; } + }); - if (e.touches[2]) { - // ignore - } else if (e.touches[1]) { - // ignore - } else if (e.touches[0]) { - var nears = r.findNearestElements(now[0], now[1], true, true); - var near = nears[0]; - - if (near != null) { - near.activate(); + defineArrowShape('tee', { + points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0], - r.touchData.start = near; - r.touchData.starts = nears; + spacing: function spacing(edge) { + return 1; + }, - if (r.nodeIsGrabbable(near)) { + gap: function gap(edge) { + return 1; + } + }); - var draggedEles = r.dragData.touchDragEles = []; - var selectedNodes = null; + defineArrowShape('square', { + points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3] + }); - r.redrawHint('eles', true); - r.redrawHint('drag', true); + defineArrowShape('diamond', { + points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0], - if (near.selected()) { - // reset drag elements, since near will be added again + gap: function gap(edge) { + return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value; + } + }); +}; - selectedNodes = cy.$(function (ele) { - return ele.selected() && r.nodeIsGrabbable(ele); - }); +module.exports = BRp; - addNodesToDrag(selectedNodes, { addToList: draggedEles }); - } else { - addNodeToDrag(near, { addToList: draggedEles }); - } +/***/ }), +/* 109 */ +/***/ (function(module, exports, __webpack_require__) { - setGrabTarget(near); +"use strict"; - var makeEvent = function makeEvent(type) { - return { - originalEvent: e, - type: type, - position: { x: now[0], y: now[1] } - }; - }; - near.emit(makeEvent('grabon')); +var util = __webpack_require__(1); - if (selectedNodes) { - selectedNodes.forEach(function (n) { - n.emit(makeEvent('grab')); - }); - } else { - near.emit(makeEvent('grab')); - } - } - } +var BRp = {}; - triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, { - position: { x: now[0], y: now[1] } - }); +[__webpack_require__(110), __webpack_require__(111), __webpack_require__(112), __webpack_require__(113), __webpack_require__(114), __webpack_require__(115), __webpack_require__(116), __webpack_require__(117), __webpack_require__(118)].forEach(function (props) { + util.extend(BRp, props); +}); - if (near == null) { - r.data.bgActivePosistion = { - x: pos[0], - y: pos[1] - }; +module.exports = BRp; - r.redrawHint('select', true); - r.redraw(); - } +/***/ }), +/* 110 */ +/***/ (function(module, exports, __webpack_require__) { - // Tap, taphold - // ----- +"use strict"; - r.touchData.singleTouchMoved = false; - r.touchData.singleTouchStartTime = +new Date(); - clearTimeout(r.touchData.tapholdTimeout); - r.touchData.tapholdTimeout = setTimeout(function () { - if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect - && !r.touchData.selecting // box selection shouldn't allow taphold through - ) { - triggerEvents(r.touchData.start, ['taphold'], e, { - position: { x: now[0], y: now[1] } - }); +var window = __webpack_require__(3); +var math = __webpack_require__(2); +var util = __webpack_require__(1); +var window = __webpack_require__(3); - if (!r.touchData.start) { - cy.$(':selected').unselect(); - } - } - }, r.tapholdDuration); - } +var BRp = {}; - if (e.touches.length >= 1) { - var sPos = r.touchData.startPosition = []; +// Project mouse +BRp.projectIntoViewport = function (clientX, clientY) { + var cy = this.cy; + var offsets = this.findContainerClientCoords(); + var offsetLeft = offsets[0]; + var offsetTop = offsets[1]; + var scale = offsets[4]; + var pan = cy.pan(); + var zoom = cy.zoom(); - for (var i = 0; i < now.length; i++) { - sPos[i] = earlier[i] = now[i]; - } + var x = ((clientX - offsetLeft) / scale - pan.x) / zoom; + var y = ((clientY - offsetTop) / scale - pan.y) / zoom; - var touch0 = e.touches[0]; + return [x, y]; +}; - r.touchData.startGPosition = [touch0.clientX, touch0.clientY]; - } - }, false); +BRp.findContainerClientCoords = function () { + if (this.containerBB) { + return this.containerBB; + } - var touchmoveHandler; - r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) { - // eslint-disable-line no-undef - var capture = r.touchData.capture; + var container = this.container; + var rect = container.getBoundingClientRect(); + var style = window.getComputedStyle(container); + var styleValue = function styleValue(name) { + return parseFloat(style.getPropertyValue(name)); + }; - if (!capture && !eventInContainer(e)) { - return; - } + var padding = { + left: styleValue('padding-left'), + right: styleValue('padding-right'), + top: styleValue('padding-top'), + bottom: styleValue('padding-bottom') + }; - var select = r.selection; - var cy = r.cy; - var now = r.touchData.now; - var earlier = r.touchData.earlier; - var zoom = cy.zoom(); + var border = { + left: styleValue('border-left-width'), + right: styleValue('border-right-width'), + top: styleValue('border-top-width'), + bottom: styleValue('border-bottom-width') + }; - if (e.touches[0]) { - var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);now[0] = pos[0];now[1] = pos[1]; - } - if (e.touches[1]) { - var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);now[2] = pos[0];now[3] = pos[1]; - } - if (e.touches[2]) { - var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);now[4] = pos[0];now[5] = pos[1]; - } + var clientWidth = container.clientWidth; + var clientHeight = container.clientHeight; - var startGPos = r.touchData.startGPosition; - var isOverThresholdDrag; + var paddingHor = padding.left + padding.right; + var paddingVer = padding.top + padding.bottom; - if (capture && e.touches[0] && startGPos) { - var disp = [];for (var j = 0; j < now.length; j++) { - disp[j] = now[j] - earlier[j]; - } - var dx = e.touches[0].clientX - startGPos[0]; - var dx2 = dx * dx; - var dy = e.touches[0].clientY - startGPos[1]; - var dy2 = dy * dy; - var dist2 = dx2 + dy2; + var borderHor = border.left + border.right; + var borderVer = border.top + border.bottom; - isOverThresholdDrag = dist2 >= r.touchTapThreshold2; - } + var scale = rect.width / (clientWidth + borderHor); - // context swipe cancelling - if (capture && r.touchData.cxt) { - e.preventDefault(); + var unscaledW = clientWidth - paddingHor; + var unscaledH = clientHeight - paddingVer; - var f1x2 = e.touches[0].clientX - offsetLeft, - f1y2 = e.touches[0].clientY - offsetTop; - var f2x2 = e.touches[1].clientX - offsetLeft, - f2y2 = e.touches[1].clientY - offsetTop; - // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 ); - var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2); - var factorSq = distance2Sq / distance1Sq; + var scaledW = rect.width - (paddingHor + borderHor) * scale; + var scaledH = rect.height - (paddingVer + borderVer) * scale; - var distThreshold = 150; - var distThresholdSq = distThreshold * distThreshold; - var factorThreshold = 1.5; - var factorThresholdSq = factorThreshold * factorThreshold; + var left = rect.left + padding.left + border.left; + var top = rect.top + padding.top + border.top; - // cancel ctx gestures if the distance b/t the fingers increases - if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) { - r.touchData.cxt = false; + return this.containerBB = [left, top, unscaledW, unscaledH, scale]; +}; - r.data.bgActivePosistion = undefined; +BRp.invalidateContainerClientCoordsCache = function () { + this.containerBB = null; +}; - r.redrawHint('select', true); +BRp.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) { + return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0]; +}; - var cxtEvt = { - originalEvent: e, - type: 'cxttapend', - position: { x: now[0], y: now[1] } - }; +BRp.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) { + var self = this; + var r = this; + var eles = r.getCachedZSortedEles(); + var near = []; // 1 node max, 1 edge max + var zoom = r.cy.zoom(); + var hasCompounds = r.cy.hasCompoundNodes(); + var edgeThreshold = (isTouch ? 24 : 8) / zoom; + var nodeThreshold = (isTouch ? 8 : 2) / zoom; + var labelThreshold = (isTouch ? 8 : 2) / zoom; + var minSqDist = Infinity; + var nearEdge; + var nearNode; - if (r.touchData.start) { - r.touchData.start.unactivate().emit(cxtEvt); + if (interactiveElementsOnly) { + eles = eles.interactive; + } - r.touchData.start = null; - } else { - cy.emit(cxtEvt); - } + function addEle(ele, sqDist) { + if (ele.isNode()) { + if (nearNode) { + return; // can't replace node + } else { + nearNode = ele; + near.push(ele); } } - // context swipe - if (capture && r.touchData.cxt) { - var cxtEvt = { - originalEvent: e, - type: 'cxtdrag', - position: { x: now[0], y: now[1] } - }; - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); - - if (r.touchData.start) { - r.touchData.start.emit(cxtEvt); + if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) { + if (nearEdge) { + // then replace existing edge + // can replace only if same z-index + if (nearEdge.pstyle('z-index').value === ele.pstyle('z-index').value) { + for (var i = 0; i < near.length; i++) { + if (near[i].isEdge()) { + near[i] = ele; + nearEdge = ele; + minSqDist = sqDist != null ? sqDist : minSqDist; + break; + } + } + } } else { - cy.emit(cxtEvt); - } - - if (r.touchData.start) { - r.touchData.start._private.grabbed = false; + near.push(ele); + nearEdge = ele; + minSqDist = sqDist != null ? sqDist : minSqDist; } - r.touchData.cxtDragged = true; + } + } - var near = r.findNearestElement(now[0], now[1], true, true); + function checkNode(node) { + var width = node.outerWidth() + 2 * nodeThreshold; + var height = node.outerHeight() + 2 * nodeThreshold; + var hw = width / 2; + var hh = height / 2; + var pos = node.position(); - if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) { + if (pos.x - hw <= x && x <= pos.x + hw // bb check x + && pos.y - hh <= y && y <= pos.y + hh // bb check y + ) { + var shape = r.nodeShapes[self.getNodeShape(node)]; - if (r.touchData.cxtOver) { - r.touchData.cxtOver.emit({ - originalEvent: e, - type: 'cxtdragout', - position: { x: now[0], y: now[1] } - }); + if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) { + addEle(node, 0); + return true; } + } + } - r.touchData.cxtOver = near; + function checkEdge(edge) { + var _p = edge._private; - if (near) { - near.emit({ - originalEvent: e, - type: 'cxtdragover', - position: { x: now[0], y: now[1] } - }); + var rs = _p.rscratch; + var styleWidth = edge.pstyle('width').pfValue; + var scale = edge.pstyle('arrow-scale').value; + var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre + var widthSq = width * width; + var width2 = width * 2; + var src = _p.source; + var tgt = _p.target; + var inEdgeBB = false; + var sqDist; + + if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') { + var pts = rs.allpts; + + for (var i = 0; i + 3 < pts.length; i += 2) { + if ((inEdgeBB = math.inLineVicinity(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], width2)) && widthSq > (sqDist = math.sqdistToFiniteLine(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3]))) { + addEle(edge, sqDist); + return true; + } + } + } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') { + var pts = rs.allpts; + for (var i = 0; i + 5 < rs.allpts.length; i += 4) { + if ((inEdgeBB = math.inBezierVicinity(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5], width2)) && widthSq > (sqDist = math.sqdistToQuadraticBezier(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5]))) { + addEle(edge, sqDist); + return true; } } + } - // box selection - } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) { - e.preventDefault(); + // if we're close to the edge but didn't hit it, maybe we hit its arrows - r.data.bgActivePosistion = undefined; + var src = src || _p.source; + var tgt = tgt || _p.target; - this.lastThreeTouch = +new Date(); + var arSize = self.getArrowWidth(styleWidth, scale); - if (!r.touchData.selecting) { - cy.emit('boxstart'); - } + var arrows = [{ name: 'source', x: rs.arrowStartX, y: rs.arrowStartY, angle: rs.srcArrowAngle }, { name: 'target', x: rs.arrowEndX, y: rs.arrowEndY, angle: rs.tgtArrowAngle }, { name: 'mid-source', x: rs.midX, y: rs.midY, angle: rs.midsrcArrowAngle }, { name: 'mid-target', x: rs.midX, y: rs.midY, angle: rs.midtgtArrowAngle }]; - r.touchData.selecting = true; + for (var i = 0; i < arrows.length; i++) { + var ar = arrows[i]; + var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value]; + var edgeWidth = edge.pstyle('width').pfValue; + if (shape.roughCollide(x, y, arSize, ar.angle, { x: ar.x, y: ar.y }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, { x: ar.x, y: ar.y }, edgeWidth, edgeThreshold)) { + addEle(edge); + return true; + } + } - r.redrawHint('select', true); + // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence) + if (hasCompounds && near.length > 0) { + checkNode(src); + checkNode(tgt); + } + } - if (!select || select.length === 0 || select[0] === undefined) { - select[0] = (now[0] + now[2] + now[4]) / 3; - select[1] = (now[1] + now[3] + now[5]) / 3; - select[2] = (now[0] + now[2] + now[4]) / 3 + 1; - select[3] = (now[1] + now[3] + now[5]) / 3 + 1; - } else { - select[2] = (now[0] + now[2] + now[4]) / 3; - select[3] = (now[1] + now[3] + now[5]) / 3; - } + function preprop(obj, name, pre) { + return util.getPrefixedProperty(obj, name, pre); + } - select[4] = 1; - r.touchData.selecting = true; + function checkLabel(ele, prefix) { + var _p = ele._private; + var th = labelThreshold; - r.redraw(); + var prefixDash; + if (prefix) { + prefixDash = prefix + '-'; + } else { + prefixDash = ''; + } - // pinch to zoom - } else if (capture && e.touches[1] && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) { - // two fingers => pinch to zoom - e.preventDefault(); + var text = ele.pstyle(prefixDash + 'label').value; + var eventsEnabled = ele.pstyle('text-events').strValue === 'yes'; - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); + if (!eventsEnabled || !text) { + return; + } - var draggedEles = r.dragData.touchDragEles; - if (draggedEles) { - r.redrawHint('drag', true); + var rstyle = _p.rstyle; + var bw = ele.pstyle('text-border-width').pfValue; + var pw = ele.pstyle('text-background-padding').pfValue; + var lw = preprop(rstyle, 'labelWidth', prefix) + bw + 2 * th + 2 * pw; + var lh = preprop(rstyle, 'labelHeight', prefix) + bw + 2 * th + 2 * pw; + var lx = preprop(rstyle, 'labelX', prefix); + var ly = preprop(rstyle, 'labelY', prefix); - for (var i = 0; i < draggedEles.length; i++) { - var de_p = draggedEles[i]._private; + var theta = preprop(_p.rscratch, 'labelAngle', prefix); - de_p.grabbed = false; - de_p.rscratch.inDragLayer = false; - } - } + var lx1 = lx - lw / 2; + var lx2 = lx + lw / 2; + var ly1 = ly - lh / 2; + var ly2 = ly + lh / 2; - // (x2, y2) for fingers 1 and 2 - var f1x2 = e.touches[0].clientX - offsetLeft, - f1y2 = e.touches[0].clientY - offsetTop; - var f2x2 = e.touches[1].clientX - offsetLeft, - f2y2 = e.touches[1].clientY - offsetTop; + if (theta) { + var cos = Math.cos(theta); + var sin = Math.sin(theta); - var distance2 = distance(f1x2, f1y2, f2x2, f2y2); - // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 ); - // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq ); - var factor = distance2 / distance1; + var rotate = function rotate(x, y) { + x = x - lx; + y = y - ly; - if (twoFingersStartInside) { - // delta finger1 - var df1x = f1x2 - f1x1; - var df1y = f1y2 - f1y1; + return { + x: x * cos - y * sin + lx, + y: x * sin + y * cos + ly + }; + }; - // delta finger 2 - var df2x = f2x2 - f2x1; - var df2y = f2y2 - f2y1; + var px1y1 = rotate(lx1, ly1); + var px1y2 = rotate(lx1, ly2); + var px2y1 = rotate(lx2, ly1); + var px2y2 = rotate(lx2, ly2); - // translation is the normalised vector of the two fingers movement - // i.e. so pinching cancels out and moving together pans - var tx = (df1x + df2x) / 2; - var ty = (df1y + df2y) / 2; + var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y]; - // adjust factor by the speed multiplier - // var speed = 1.5; - // if( factor > 1 ){ - // factor = (factor - 1) * speed + 1; - // } else { - // factor = 1 - (1 - factor) * speed; - // } + if (math.pointInsidePolygonPoints(x, y, points)) { + addEle(ele); + return true; + } + } else { + // do a cheaper bb check + var bb = { + w: lw, + h: lh, + x1: lx1, + x2: lx2, + y1: ly1, + y2: ly2 + }; - // now calculate the zoom - var zoom1 = cy.zoom(); - var zoom2 = zoom1 * factor; - var pan1 = cy.pan(); + if (math.inBoundingBox(bb, x, y)) { + addEle(ele); + return true; + } + } + } - // the model center point converted to the current rendered pos - var ctrx = modelCenter1[0] * zoom1 + pan1.x; - var ctry = modelCenter1[1] * zoom1 + pan1.y; + for (var i = eles.length - 1; i >= 0; i--) { + // reverse order for precedence + var ele = eles[i]; - var pan2 = { - x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx, - y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry - }; + if (ele.isNode()) { + checkNode(ele) || checkLabel(ele); + } else { + // then edge + checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target'); + } + } - // remove dragged eles - if (r.touchData.start && r.touchData.start.active()) { - var draggedEles = r.dragData.touchDragEles; + return near; +}; - freeDraggedElements(draggedEles); +// 'Give me everything from this box' +BRp.getAllInBox = function (x1, y1, x2, y2) { + var eles = this.getCachedZSortedEles().interactive; + var box = []; - r.redrawHint('drag', true); - r.redrawHint('eles', true); + var x1c = Math.min(x1, x2); + var x2c = Math.max(x1, x2); + var y1c = Math.min(y1, y2); + var y2c = Math.max(y1, y2); - r.touchData.start.unactivate().emit('free'); - } + x1 = x1c; + x2 = x2c; + y1 = y1c; + y2 = y2c; - cy.viewport({ - zoom: zoom2, - pan: pan2, - cancelOnFailedZoom: true - }); + var boxBb = math.makeBoundingBox({ + x1: x1, y1: y1, + x2: x2, y2: y2 + }); - distance1 = distance2; - f1x1 = f1x2; - f1y1 = f1y2; - f2x1 = f2x2; - f2y1 = f2y2; + for (var e = 0; e < eles.length; e++) { + var ele = eles[e]; - r.pinching = true; - } + if (ele.isNode()) { + var node = ele; + var nodeBb = node.boundingBox({ + includeNodes: true, + includeEdges: false, + includeLabels: false + }); - // Re-project - if (e.touches[0]) { - var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);now[0] = pos[0];now[1] = pos[1]; - } - if (e.touches[1]) { - var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);now[2] = pos[0];now[3] = pos[1]; - } - if (e.touches[2]) { - var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);now[4] = pos[0];now[5] = pos[1]; + if (math.boundingBoxesIntersect(boxBb, nodeBb) && !math.boundingBoxInBoundingBox(nodeBb, boxBb)) { + box.push(node); } - } else if (e.touches[0]) { - var start = r.touchData.start; - var last = r.touchData.last; - var near; + } else { + var edge = ele; + var _p = edge._private; + var rs = _p.rscratch; - if (!r.hoverData.draggingEles && !r.swipePanning) { - near = r.findNearestElement(now[0], now[1], true, true); + if (rs.startX != null && rs.startY != null && !math.inBoundingBox(boxBb, rs.startX, rs.startY)) { + continue; } - - if (capture && start != null) { - e.preventDefault(); + if (rs.endX != null && rs.endY != null && !math.inBoundingBox(boxBb, rs.endX, rs.endY)) { + continue; } - // dragging nodes - if (capture && start != null && r.nodeIsDraggable(start)) { + if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') { - if (isOverThresholdDrag) { - // then dragging can happen - var draggedEles = r.dragData.touchDragEles; - var justStartedDrag = !r.dragData.didDrag; + var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts; + var allInside = true; - if (justStartedDrag) { - addNodesToDrag(cy.collection(draggedEles), { inDragLayer: true }); + for (var i = 0; i < pts.length; i++) { + if (!math.pointInBoundingBox(boxBb, pts[i])) { + allInside = false; + break; } + } - for (var k = 0; k < draggedEles.length; k++) { - var draggedEle = draggedEles[k]; - - if (r.nodeIsDraggable(draggedEle) && draggedEle.grabbed()) { - r.dragData.didDrag = true; - var dPos = draggedEle.position(); - - if (is.number(disp[0]) && is.number(disp[1])) { - dPos.x += disp[0]; - dPos.y += disp[1]; - } + if (allInside) { + box.push(edge); + } + } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') { + box.push(edge); + } + } + } - if (justStartedDrag) { - r.redrawHint('eles', true); + return box; +}; - var dragDelta = r.touchData.dragDelta; +module.exports = BRp; - if (dragDelta && is.number(dragDelta[0]) && is.number(dragDelta[1])) { - dPos.x += dragDelta[0]; - dPos.y += dragDelta[1]; - } - } - } - } +/***/ }), +/* 111 */ +/***/ (function(module, exports, __webpack_require__) { - var tcol = cy.collection(draggedEles); +"use strict"; - tcol.dirtyCompoundBoundsCache(); - tcol.emit('position drag'); - r.hoverData.draggingEles = true; +var math = __webpack_require__(2); - r.redrawHint('drag', true); +var BRp = {}; - if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) { +BRp.calculateArrowAngles = function (edge) { + var rs = edge._private.rscratch; + var isHaystack = rs.edgeType === 'haystack'; + var isBezier = rs.edgeType === 'bezier'; + var isMultibezier = rs.edgeType === 'multibezier'; + var isSegments = rs.edgeType === 'segments'; + var isCompound = rs.edgeType === 'compound'; + var isSelf = rs.edgeType === 'self'; - r.redrawHint('eles', true); - } + // Displacement gives direction for arrowhead orientation + var dispX, dispY; + var startX, startY, endX, endY, midX, midY; - r.redraw(); - } else { - // otherise keep track of drag delta for later - var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || []; + if (isHaystack) { + startX = rs.haystackPts[0]; + startY = rs.haystackPts[1]; + endX = rs.haystackPts[2]; + endY = rs.haystackPts[3]; + } else { + startX = rs.arrowStartX; + startY = rs.arrowStartY; + endX = rs.arrowEndX; + endY = rs.arrowEndY; + } - if (dragDelta.length === 0) { - dragDelta.push(disp[0]); - dragDelta.push(disp[1]); - } else { - dragDelta[0] += disp[0]; - dragDelta[1] += disp[1]; - } - } - } + midX = rs.midX; + midY = rs.midY; - // touchmove - { - triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, { - position: { x: now[0], y: now[1] } - }); + // source + // - if ((!start || !start.grabbed()) && near != last) { - if (last) { - last.emit({ originalEvent: e, type: 'tapdragout', position: { x: now[0], y: now[1] } }); - } - if (near) { - near.emit({ originalEvent: e, type: 'tapdragover', position: { x: now[0], y: now[1] } }); - } - } + if (isSegments) { + dispX = startX - rs.segpts[0]; + dispY = startY - rs.segpts[1]; + } else if (isMultibezier || isCompound || isSelf || isBezier) { + var pts = rs.allpts; + var bX = math.qbezierAt(pts[0], pts[2], pts[4], 0.1); + var bY = math.qbezierAt(pts[1], pts[3], pts[5], 0.1); - r.touchData.last = near; - } + dispX = startX - bX; + dispY = startY - bY; + } else { + dispX = startX - midX; + dispY = startY - midY; + } - // check to cancel taphold - if (capture) { - for (var i = 0; i < now.length; i++) { - if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) { + rs.srcArrowAngle = math.getAngleFromDisp(dispX, dispY); - r.touchData.singleTouchMoved = true; - } - } - } + // mid target + // - // panning - if (capture && (start == null || start.isEdge()) && cy.panningEnabled() && cy.userPanningEnabled()) { + var midX = rs.midX; + var midY = rs.midY; - var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts); + if (isHaystack) { + midX = (startX + endX) / 2; + midY = (startY + endY) / 2; + } - if (allowPassthrough) { - e.preventDefault(); + dispX = endX - startX; + dispY = endY - startY; - if (r.swipePanning) { - cy.panBy({ - x: disp[0] * zoom, - y: disp[1] * zoom - }); - } else if (isOverThresholdDrag) { - r.swipePanning = true; + if (isSegments) { + var pts = rs.allpts; - cy.panBy({ - x: dx * zoom, - y: dy * zoom - }); + if (pts.length / 2 % 2 === 0) { + var i2 = pts.length / 2; + var i1 = i2 - 2; - if (start) { - start.unactivate(); + dispX = pts[i2] - pts[i1]; + dispY = pts[i2 + 1] - pts[i1 + 1]; + } else { + var i2 = pts.length / 2 - 1; + var i1 = i2 - 2; + var i3 = i2 + 2; - if (!r.data.bgActivePosistion) { - r.data.bgActivePosistion = math.array2point(r.touchData.startPosition); - } + dispX = pts[i2] - pts[i1]; + dispY = pts[i2 + 1] - pts[i1 + 1]; + } + } else if (isMultibezier || isCompound || isSelf) { + var pts = rs.allpts; + var cpts = rs.ctrlpts; + var bp0x, bp0y; + var bp1x, bp1y; - r.redrawHint('select', true); + if (cpts.length / 2 % 2 === 0) { + var p0 = pts.length / 2 - 1; // startpt + var ic = p0 + 2; + var p1 = ic + 2; - r.touchData.start = null; - } - } - } + bp0x = math.qbezierAt(pts[p0], pts[ic], pts[p1], 0.0); + bp0y = math.qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0); - // Re-project - var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY); - now[0] = pos[0];now[1] = pos[1]; - } - } + bp1x = math.qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001); + bp1y = math.qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001); + } else { + var ic = pts.length / 2 - 1; // ctrpt + var p0 = ic - 2; // startpt + var p1 = ic + 2; // endpt - for (var j = 0; j < now.length; j++) { - earlier[j] = now[j]; + bp0x = math.qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999); + bp0y = math.qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999); + + bp1x = math.qbezierAt(pts[p0], pts[ic], pts[p1], 0.5); + bp1y = math.qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5); } - //r.redraw(); - }, false); - var touchcancelHandler; - r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) { - // eslint-disable-line no-undef - var start = r.touchData.start; + dispX = bp1x - bp0x; + dispY = bp1y - bp0y; + } - r.touchData.capture = false; + rs.midtgtArrowAngle = math.getAngleFromDisp(dispX, dispY); - if (start) { - start.unactivate(); - } - }); + rs.midDispX = dispX; + rs.midDispY = dispY; - var touchendHandler; - r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) { - // eslint-disable-line no-undef - var start = r.touchData.start; + // mid source + // - var capture = r.touchData.capture; + dispX *= -1; + dispY *= -1; - if (capture) { - r.touchData.capture = false; + if (isSegments) { + var pts = rs.allpts; - e.preventDefault(); + if (pts.length / 2 % 2 === 0) { + // already ok } else { - return; + var i2 = pts.length / 2 - 1; + var i3 = i2 + 2; + + dispX = -(pts[i3] - pts[i2]); + dispY = -(pts[i3 + 1] - pts[i2 + 1]); } + } - var select = r.selection; + rs.midsrcArrowAngle = math.getAngleFromDisp(dispX, dispY); - r.swipePanning = false; - r.hoverData.draggingEles = false; + // target + // - var cy = r.cy; - var zoom = cy.zoom(); - var now = r.touchData.now; - var earlier = r.touchData.earlier; + if (isSegments) { + dispX = endX - rs.segpts[rs.segpts.length - 2]; + dispY = endY - rs.segpts[rs.segpts.length - 1]; + } else if (isMultibezier || isCompound || isSelf || isBezier) { + var pts = rs.allpts; + var l = pts.length; + var bX = math.qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9); + var bY = math.qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9); - if (e.touches[0]) { - var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);now[0] = pos[0];now[1] = pos[1]; - } - if (e.touches[1]) { - var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);now[2] = pos[0];now[3] = pos[1]; - } - if (e.touches[2]) { - var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);now[4] = pos[0];now[5] = pos[1]; - } + dispX = endX - bX; + dispY = endY - bY; + } else { + dispX = endX - midX; + dispY = endY - midY; + } - if (start) { - start.unactivate(); - } + rs.tgtArrowAngle = math.getAngleFromDisp(dispX, dispY); +}; - var ctxTapend; - if (r.touchData.cxt) { - ctxTapend = { - originalEvent: e, - type: 'cxttapend', - position: { x: now[0], y: now[1] } - }; +BRp.getArrowWidth = BRp.getArrowHeight = function (edgeWidth, scale) { + var cache = this.arrowWidthCache = this.arrowWidthCache || {}; - if (start) { - start.emit(ctxTapend); - } else { - cy.emit(ctxTapend); - } + var cachedVal = cache[edgeWidth + ', ' + scale]; + if (cachedVal) { + return cachedVal; + } - if (!r.touchData.cxtDragged) { - var ctxTap = { - originalEvent: e, - type: 'cxttap', - position: { x: now[0], y: now[1] } - }; + cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale; + cache[edgeWidth + ', ' + scale] = cachedVal; - if (start) { - start.emit(ctxTap); - } else { - cy.emit(ctxTap); - } - } + return cachedVal; +}; - if (r.touchData.start) { - r.touchData.start._private.grabbed = false; - } - r.touchData.cxt = false; - r.touchData.start = null; +module.exports = BRp; - r.redraw(); - return; - } +/***/ }), +/* 112 */ +/***/ (function(module, exports, __webpack_require__) { - // no more box selection if we don't have three fingers - if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) { - r.touchData.selecting = false; +"use strict"; - var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3])); - select[0] = undefined; - select[1] = undefined; - select[2] = undefined; - select[3] = undefined; - select[4] = 0; +var math = __webpack_require__(2); +var is = __webpack_require__(0); - r.redrawHint('select', true); +var BRp = {}; - cy.emit('boxend'); +BRp.findEdgeControlPoints = function (edges) { + if (!edges || edges.length === 0) { + return; + } - var eleWouldBeSelected = function eleWouldBeSelected(ele) { - return ele.selectable() && !ele.selected(); - }; + var r = this; + var cy = r.cy; + var hasCompounds = cy.hasCompoundNodes(); + var hashTable = {}; + var pairIds = []; + var haystackEdges = []; - box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect'); + // create a table of edge (src, tgt) => list of edges between them + var pairId; + for (var i = 0; i < edges.length; i++) { + var edge = edges[i]; + var _p = edge._private; + var data = _p.data; + var curveStyle = edge.pstyle('curve-style').value; + var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments'; + var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier'; - if (box.nonempty()) { - r.redrawHint('eles', true); - } + // ignore edges who are not to be displayed + // they shouldn't take up space + if (edge.pstyle('display').value === 'none') { + continue; + } - r.redraw(); + if (curveStyle === 'haystack') { + haystackEdges.push(edge); + continue; } - if (start != null) { - start.unactivate(); + var srcId = data.source; + var tgtId = data.target; + + pairId = srcId > tgtId ? tgtId + '$-$' + srcId : srcId + '$-$' + tgtId; + + if (edgeIsUnbundled) { + pairId = 'unbundled' + '$-$' + data.id; } - if (e.touches[2]) { - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); - } else if (e.touches[1]) { - // ignore - } else if (e.touches[0]) { - // ignore + var tableEntry = hashTable[pairId]; - // Last touch released - } else if (!e.touches[0]) { + if (tableEntry == null) { + tableEntry = hashTable[pairId] = []; + pairIds.push(pairId); + } - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); + tableEntry.push(edge); - var draggedEles = r.dragData.touchDragEles; + if (edgeIsUnbundled) { + tableEntry.hasUnbundled = true; + } - if (start != null) { + if (edgeIsBezier) { + tableEntry.hasBezier = true; + } + } - var startWasGrabbed = start._private.grabbed; + var src, tgt, srcPos, tgtPos, srcW, srcH, tgtW, tgtH, srcShape, tgtShape; + var vectorNormInverse; + var badBezier; - freeDraggedElements(draggedEles); + // for each pair (src, tgt), create the ctrl pts + // Nested for loop is OK; total number of iterations for both loops = edgeCount + for (var p = 0; p < pairIds.length; p++) { + pairId = pairIds[p]; + var pairEdges = hashTable[pairId]; - r.redrawHint('drag', true); - r.redrawHint('eles', true); + // for each pair id, the edges should be sorted by index + pairEdges.sort(function (edge1, edge2) { + return edge1.poolIndex() - edge2.poolIndex(); + }); - if (startWasGrabbed) { - start.emit('free'); - } + src = pairEdges[0]._private.source; + tgt = pairEdges[0]._private.target; - triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, { - position: { x: now[0], y: now[1] } - }); + // make sure src/tgt distinction is consistent for bundled edges + if (!pairEdges.hasUnbundled && src.id() > tgt.id()) { + var temp = src; + src = tgt; + tgt = temp; + } - start.unactivate(); + srcPos = src.position(); + tgtPos = tgt.position(); - r.touchData.start = null; - } else { - var near = r.findNearestElement(now[0], now[1], true, true); + srcW = src.outerWidth(); + srcH = src.outerHeight(); - triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, { - position: { x: now[0], y: now[1] } - }); - } + tgtW = tgt.outerWidth(); + tgtH = tgt.outerHeight(); - var dx = r.touchData.startPosition[0] - now[0]; - var dx2 = dx * dx; - var dy = r.touchData.startPosition[1] - now[1]; - var dy2 = dy * dy; - var dist2 = dx2 + dy2; - var rdist2 = dist2 * zoom * zoom; + srcShape = r.nodeShapes[this.getNodeShape(src)]; + tgtShape = r.nodeShapes[this.getNodeShape(tgt)]; - // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance - if (start != null && !r.dragData.didDrag // didn't drag nodes around - && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection - ) { + badBezier = false; - if (cy.selectionType() === 'single') { - cy.$(':selected').unmerge(start).unselect(); - start.select(); - } else { - if (start.selected()) { - start.unselect(); - } else { - start.select(); - } - } + var edge; + var edge_p; + var rs; - r.redrawHint('eles', true); - } + var dirCounts = { + 'north': 0, + 'west': 0, + 'south': 0, + 'east': 0, + 'northwest': 0, + 'southwest': 0, + 'northeast': 0, + 'southeast': 0 + }; - // Tap event, roughly same as mouse click event for touch - if (!r.touchData.singleTouchMoved) { - triggerEvents(start, ['tap', 'vclick'], e, { - position: { x: now[0], y: now[1] } - }); - } + var srcX2 = srcPos.x; + var srcY2 = srcPos.y; + var srcW2 = srcW; + var srcH2 = srcH; - r.touchData.singleTouchMoved = true; - } + var tgtX2 = tgtPos.x; + var tgtY2 = tgtPos.y; + var tgtW2 = tgtW; + var tgtH2 = tgtH; - for (var j = 0; j < now.length; j++) { - earlier[j] = now[j]; - } + var numEdges2 = pairEdges.length; - r.dragData.didDrag = false; // reset for next mousedown + for (var i = 0; i < pairEdges.length; i++) { + edge = pairEdges[i]; + edge_p = edge._private; + rs = edge_p.rscratch; - if (e.touches.length === 0) { - r.touchData.dragDelta = []; - r.touchData.startPosition = null; - r.touchData.startGPosition = null; - } + var edgeIndex1 = rs.lastEdgeIndex; + var edgeIndex2 = i; - if (e.touches.length < 2) { - r.pinching = false; - r.redrawHint('eles', true); - r.redraw(); - } + var numEdges1 = rs.lastNumEdges; - //r.redraw(); - }, false); + var curveStyle = edge.pstyle('curve-style').value; - // fallback compatibility layer for ms pointer events - if (typeof TouchEvent === 'undefined') { + var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments'; - var pointers = []; + // whether the normalised pair order is the reverse of the edge's src-tgt order + var edgeIsSwapped = src.id() !== edge.source().id(); + + var ctrlptDists = edge.pstyle('control-point-distances'); + var loopDir = edge.pstyle('loop-direction').pfValue; + var loopSwp = edge.pstyle('loop-sweep').pfValue; + var ctrlptWs = edge.pstyle('control-point-weights'); + var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1; + var stepSize = edge.pstyle('control-point-step-size').pfValue; + var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined; + var ctrlptWeight = ctrlptWs.value[0]; + var edgeDistances = edge.pstyle('edge-distances').value; + var segmentWs = edge.pstyle('segment-weights'); + var segmentDs = edge.pstyle('segment-distances'); + var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length); + var srcEndpt = edge.pstyle('source-endpoint').value; + var tgtEndpt = edge.pstyle('target-endpoint').value; + var srcArrShape = edge.pstyle('source-arrow-shape').value; + var tgtArrShape = edge.pstyle('target-arrow-shape').value; + var arrowScale = edge.pstyle('arrow-scale').value; + var lineWidth = edge.pstyle('width').pfValue; - var makeTouch = function makeTouch(e) { - return { - clientX: e.clientX, - clientY: e.clientY, - force: 1, - identifier: e.pointerId, - pageX: e.pageX, - pageY: e.pageY, - radiusX: e.width / 2, - radiusY: e.height / 2, - screenX: e.screenX, - screenY: e.screenY, - target: e.target - }; - }; + var srcX1 = rs.lastSrcCtlPtX; + var srcY1 = rs.lastSrcCtlPtY; + var srcW1 = rs.lastSrcCtlPtW; + var srcH1 = rs.lastSrcCtlPtH; - var makePointer = function makePointer(e) { - return { - event: e, - touch: makeTouch(e) - }; - }; + var tgtX1 = rs.lastTgtCtlPtX; + var tgtY1 = rs.lastTgtCtlPtY; + var tgtW1 = rs.lastTgtCtlPtW; + var tgtH1 = rs.lastTgtCtlPtH; - var addPointer = function addPointer(e) { - pointers.push(makePointer(e)); - }; + var curveStyle1 = rs.lastCurveStyle; + var curveStyle2 = curveStyle; - var removePointer = function removePointer(e) { - for (var i = 0; i < pointers.length; i++) { - var p = pointers[i]; + var ctrlptDists1 = rs.lastCtrlptDists; + var ctrlptDists2 = ctrlptDists ? ctrlptDists.strValue : null; - if (p.event.pointerId === e.pointerId) { - pointers.splice(i, 1); - return; - } - } - }; + var ctrlptWs1 = rs.lastCtrlptWs; + var ctrlptWs2 = ctrlptWs.strValue; - var updatePointer = function updatePointer(e) { - var p = pointers.filter(function (p) { - return p.event.pointerId === e.pointerId; - })[0]; + var segmentWs1 = rs.lastSegmentWs; + var segmentWs2 = segmentWs.strValue; - p.event = e; - p.touch = makeTouch(e); - }; + var segmentDs1 = rs.lastSegmentDs; + var segmentDs2 = segmentDs.strValue; - var addTouchesToEvent = function addTouchesToEvent(e) { - e.touches = pointers.map(function (p) { - return p.touch; - }); - }; + var stepSize1 = rs.lastStepSize; + var stepSize2 = stepSize; - var pointerIsMouse = function pointerIsMouse(e) { - return e.pointerType === 'mouse' || e.pointerType === 4; - }; + var loopDir1 = rs.lastLoopDir; + var loopDir2 = loopDir; - r.registerBinding(r.container, 'pointerdown', function (e) { - if (pointerIsMouse(e)) { - return; - } // mouse already handled + var loopSwp1 = rs.lastLoopSwp; + var loopSwp2 = loopSwp; - e.preventDefault(); + var edgeDistances1 = rs.lastEdgeDistances; + var edgeDistances2 = edgeDistances; - addPointer(e); + var srcEndpt1 = rs.lastSrcEndpt; + var srcEndpt2 = srcEndpt; - addTouchesToEvent(e); - touchstartHandler(e); - }); + var tgtEndpt1 = rs.lastTgtEndpt; + var tgtEndpt2 = tgtEndpt; - r.registerBinding(r.container, 'pointerup', function (e) { - if (pointerIsMouse(e)) { - return; - } // mouse already handled + var srcArr1 = rs.lastSrcArr; + var srcArr2 = srcArrShape; - removePointer(e); + var tgtArr1 = rs.lastTgtArr; + var tgtArr2 = tgtArrShape; - addTouchesToEvent(e); - touchendHandler(e); - }); + var lineW1 = rs.lastLineW; + var lineW2 = lineWidth; - r.registerBinding(r.container, 'pointercancel', function (e) { - if (pointerIsMouse(e)) { - return; - } // mouse already handled + var arrScl1 = rs.lastArrScl; + var arrScl2 = arrowScale; - removePointer(e); + if (badBezier) { + rs.badBezier = true; + } else { + rs.badBezier = false; + } - addTouchesToEvent(e); - touchcancelHandler(e); - }); + var ptCacheHit; - r.registerBinding(r.container, 'pointermove', function (e) { - if (pointerIsMouse(e)) { - return; - } // mouse already handled + if (srcX1 === srcX2 && srcY1 === srcY2 && srcW1 === srcW2 && srcH1 === srcH2 && tgtX1 === tgtX2 && tgtY1 === tgtY2 && tgtW1 === tgtW2 && tgtH1 === tgtH2 && curveStyle1 === curveStyle2 && ctrlptDists1 === ctrlptDists2 && ctrlptWs1 === ctrlptWs2 && segmentWs1 === segmentWs2 && segmentDs1 === segmentDs2 && stepSize1 === stepSize2 && loopDir1 === loopDir2 && loopSwp1 === loopSwp2 && edgeDistances1 === edgeDistances2 && srcEndpt1 === srcEndpt2 && tgtEndpt1 === tgtEndpt2 && srcArr1 === srcArr2 && tgtArr1 === tgtArr2 && lineW1 === lineW2 && arrScl1 === arrScl2 && (edgeIndex1 === edgeIndex2 && numEdges1 === numEdges2 || edgeIsUnbundled)) { + ptCacheHit = true; // then the control points haven't changed and we can skip calculating them + } else { + ptCacheHit = false; - e.preventDefault(); + rs.lastSrcCtlPtX = srcX2; + rs.lastSrcCtlPtY = srcY2; + rs.lastSrcCtlPtW = srcW2; + rs.lastSrcCtlPtH = srcH2; + rs.lastTgtCtlPtX = tgtX2; + rs.lastTgtCtlPtY = tgtY2; + rs.lastTgtCtlPtW = tgtW2; + rs.lastTgtCtlPtH = tgtH2; + rs.lastEdgeIndex = edgeIndex2; + rs.lastNumEdges = numEdges2; + rs.lastCurveStyle = curveStyle2; + rs.lastCtrlptDists = ctrlptDists2; + rs.lastCtrlptWs = ctrlptWs2; + rs.lastSegmentDs = segmentDs2; + rs.lastSegmentWs = segmentWs2; + rs.lastStepSize = stepSize2; + rs.lastLoopDir = loopDir2; + rs.lastLoopSwp = loopSwp2; + rs.lastEdgeDistances = edgeDistances2; + rs.lastSrcEndpt = srcEndpt2; + rs.lastTgtEndpt = tgtEndpt2; + rs.lastSrcArr = srcArr2; + rs.lastTgtArr = tgtArr2; + rs.lastLineW = lineW2; + rs.lastArrScl = arrScl2; + } - updatePointer(e); + if (!ptCacheHit) { - addTouchesToEvent(e); - touchmoveHandler(e); - }); - } -}; + if (!pairEdges.calculatedIntersection && src !== tgt && (pairEdges.hasBezier || pairEdges.hasUnbundled)) { -module.exports = BRp; + pairEdges.calculatedIntersection = true; -/***/ }), -/* 99 */ -/***/ (function(module, exports, __webpack_require__) { + // pt outside src shape to calc distance/displacement from src to tgt + var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0); -"use strict"; + pairEdges.srcIntn = srcOutside; + // pt outside tgt shape to calc distance/displacement from src to tgt + var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0); -var math = __webpack_require__(2); + pairEdges.tgtIntn = tgtOutside; -var BRp = {}; + var midptSrcPts = { + x1: srcOutside[0], + x2: tgtOutside[0], + y1: srcOutside[1], + y2: tgtOutside[1] + }; -BRp.generatePolygon = function (name, points) { - return this.nodeShapes[name] = { - renderer: this, + var posPts = { + x1: srcPos.x, + x2: tgtPos.x, + y1: srcPos.y, + y2: tgtPos.y + }; - name: name, + var dy = tgtOutside[1] - srcOutside[1]; + var dx = tgtOutside[0] - srcOutside[0]; + var l = Math.sqrt(dx * dx + dy * dy); - points: points, + var vector = { + x: dx, + y: dy + }; - draw: function draw(context, centerX, centerY, width, height) { - this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points); - }, + var vectorNorm = { + x: vector.x / l, + y: vector.y / l + }; + vectorNormInverse = { + x: -vectorNorm.y, + y: vectorNorm.x + }; - intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { - return math.polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding); - }, + // if node shapes overlap, then no ctrl pts to draw + if (tgtShape.checkPoint(srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y) && srcShape.checkPoint(tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y)) { + vectorNormInverse = {}; + badBezier = true; + } + } - checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { - return math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding); - } - }; -}; + if (!edgeIsSwapped) { + rs.srcIntn = pairEdges.srcIntn; + rs.tgtIntn = pairEdges.tgtIntn; + } else { + // ensure that the per-edge cached value for intersections are correct for swapped bundled edges + rs.srcIntn = pairEdges.tgtIntn; + rs.tgtIntn = pairEdges.srcIntn; + } -BRp.generateEllipse = function () { - return this.nodeShapes['ellipse'] = { - renderer: this, + if (src === tgt) { + // Self-edge - name: 'ellipse', + rs.edgeType = 'self'; - draw: function draw(context, centerX, centerY, width, height) { - this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); - }, + var j = i; + var loopDist = stepSize; - intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { - return math.intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding); - }, + if (edgeIsUnbundled) { + j = 0; + loopDist = ctrlptDist; + } - checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { - return math.checkInEllipse(x, y, width, height, centerX, centerY, padding); - } - }; -}; + var loopAngle = loopDir - Math.PI / 2; + var outAngle = loopAngle - loopSwp / 2; + var inAngle = loopAngle + loopSwp / 2; -BRp.generateRoundRectangle = function () { - return this.nodeShapes['roundrectangle'] = { - renderer: this, + // increase by step size for overlapping loops, keyed on direction and sweep values + var dc = String(loopDir + '_' + loopSwp); + j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc]; - name: 'roundrectangle', + rs.ctrlpts = [srcPos.x + Math.cos(outAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.y + Math.sin(outAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.x + Math.cos(inAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.y + Math.sin(inAngle) * 1.4 * loopDist * (j / 3 + 1)]; + } else if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src))) { + // Compound edge - points: math.generateUnitNgonPointsFitToSquare(4, 0), + rs.edgeType = 'compound'; - draw: function draw(context, centerX, centerY, width, height) { - this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); - }, + // because the line approximation doesn't apply for compound beziers + // (loop/self edges are already elided b/c of cheap src==tgt check) + rs.badBezier = false; - intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { - return math.roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding); - }, + var j = i; + var loopDist = stepSize; - checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { + if (edgeIsUnbundled) { + j = 0; + loopDist = ctrlptDist; + } - var cornerRadius = math.getRoundRectangleRadius(width, height); - var diam = cornerRadius * 2; + var loopW = 50; - // Check hBox - if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) { - return true; - } + var loopaPos = { + x: srcPos.x - srcW / 2, + y: srcPos.y - srcH / 2 + }; - // Check vBox - if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) { - return true; - } + var loopbPos = { + x: tgtPos.x - tgtW / 2, + y: tgtPos.y - tgtH / 2 + }; - // Check top left quarter circle - if (math.checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) { + var loopPos = { + x: Math.min(loopaPos.x, loopbPos.x), + y: Math.min(loopaPos.y, loopbPos.y) + }; - return true; - } + // avoids cases with impossible beziers + var minCompoundStretch = 0.5; + var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01)); + var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01)); - // Check top right quarter circle - if (math.checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) { + rs.ctrlpts = [loopPos.x, loopPos.y - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchA, loopPos.x - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchB, loopPos.y]; + } else if (curveStyle === 'segments') { + // Segments (multiple straight lines) - return true; - } + rs.edgeType = 'segments'; + rs.segpts = []; - // Check bottom right quarter circle - if (math.checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) { + for (var s = 0; s < segmentsN; s++) { + var w = segmentWs.pfValue[s]; + var d = segmentDs.pfValue[s]; - return true; - } + var w1 = 1 - w; + var w2 = w; - // Check bottom left quarter circle - if (math.checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) { + var midptPts = edgeDistances === 'node-position' ? posPts : midptSrcPts; - return true; - } + var adjustedMidpt = { + x: midptPts.x1 * w1 + midptPts.x2 * w2, + y: midptPts.y1 * w1 + midptPts.y2 * w2 + }; - return false; - } - }; -}; + rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d); + } -BRp.generateCutRectangle = function () { - return this.nodeShapes['cutrectangle'] = { - renderer: this, + // Straight edge + } else if (pairEdges.length % 2 === 1 && i === Math.floor(pairEdges.length / 2) && !edgeIsUnbundled) { - name: 'cutrectangle', + rs.edgeType = 'straight'; + } else { + // (Multi)bezier - cornerLength: math.getCutRectangleCornerLength(), + var multi = edgeIsUnbundled; - points: math.generateUnitNgonPointsFitToSquare(4, 0), + rs.edgeType = multi ? 'multibezier' : 'bezier'; + rs.ctrlpts = []; - draw: function draw(context, centerX, centerY, width, height) { - this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); - }, + for (var b = 0; b < bezierN; b++) { + var normctrlptDist = (0.5 - pairEdges.length / 2 + i) * stepSize; + var manctrlptDist; + var sign = math.signum(normctrlptDist); - generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) { - var cl = this.cornerLength; - var hh = height / 2; - var hw = width / 2; - var xBegin = centerX - hw; - var xEnd = centerX + hw; - var yBegin = centerY - hh; - var yEnd = centerY + hh; + if (multi) { + ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size + ctrlptWeight = ctrlptWs.value[b]; + } - // points are in clockwise order, inner (imaginary) triangle pt on [4, 5] - return { - topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl], - topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl], - bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl], - bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl] - }; - }, + if (edgeIsUnbundled) { + // multi or single unbundled + manctrlptDist = ctrlptDist; + } else { + manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined; + } - intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { - var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY); - var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]); + var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist; - return math.polygonIntersectLine(x, y, pts, nodeX, nodeY); - }, + var w1 = 1 - ctrlptWeight; + var w2 = ctrlptWeight; - checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { - // Check hBox - if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) { - return true; - } + if (edgeIsSwapped) { + var temp = w1; + w1 = w2; + w2 = temp; + } - // Check vBox - if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) { - return true; - } - var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY); - return math.pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || math.pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || math.pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || math.pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft); - } + var midptPts = edgeDistances === 'node-position' ? posPts : midptSrcPts; - }; -}; + var adjustedMidpt = { + x: midptPts.x1 * w1 + midptPts.x2 * w2, + y: midptPts.y1 * w1 + midptPts.y2 * w2 + }; -BRp.generateBarrel = function () { - return this.nodeShapes['barrel'] = { - renderer: this, + rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint); + } + } - name: 'barrel', + // find endpts for edge + this.findEndpoints(edge); - points: math.generateUnitNgonPointsFitToSquare(4, 0), + var badStart = !is.number(rs.startX) || !is.number(rs.startY); + var badAStart = !is.number(rs.arrowStartX) || !is.number(rs.arrowStartY); + var badEnd = !is.number(rs.endX) || !is.number(rs.endY); + var badAEnd = !is.number(rs.arrowEndX) || !is.number(rs.arrowEndY); - draw: function draw(context, centerX, centerY, width, height) { - this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); - }, + var minCpADistFactor = 3; + var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth; + var minCpADist = minCpADistFactor * arrowW; - intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { - var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY); + if (rs.edgeType === 'bezier') { + var startACpDist = math.dist({ x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.startX, y: rs.startY }); + var closeStartACp = startACpDist < minCpADist; + var endACpDist = math.dist({ x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.endX, y: rs.endY }); + var closeEndACp = endACpDist < minCpADist; - var pts = [].concat(bPts.topLeft, bPts.topRight, bPts.bottomRight, bPts.bottomLeft); + var overlapping = false; - return math.polygonIntersectLine(x, y, pts, nodeX, nodeY); - }, + if (badStart || badAStart || closeStartACp) { + overlapping = true; - generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) { - var hh = height / 2; - var hw = width / 2; - var xBegin = centerX - hw; - var xEnd = centerX + hw; - var yBegin = centerY - hh; - var yEnd = centerY + hh; + // project control point along line from src centre to outside the src shape + // (otherwise intersection will yield nothing) + var cpD = { // delta + x: rs.ctrlpts[0] - srcPos.x, + y: rs.ctrlpts[1] - srcPos.y + }; + var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line + var cpM = { // normalised delta + x: cpD.x / cpL, + y: cpD.y / cpL + }; + var radius = Math.max(srcW, srcH); + var cpProj = { // *2 radius guarantees outside shape + x: rs.ctrlpts[0] + cpM.x * 2 * radius, + y: rs.ctrlpts[1] + cpM.y * 2 * radius + }; - var curveConstants = math.getBarrelCurveConstants(width, height); - var hOffset = curveConstants.heightOffset; - var wOffset = curveConstants.widthOffset; - var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; + var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0); - // points are in clockwise order, inner (imaginary) control pt on [4, 5] - var pts = { - topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin], - topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset], - bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd], - bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset] - }; + if (closeStartACp) { + rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist); + rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist); + } else { + rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist; + rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist; + } + } - pts.topLeft.isTop = true; - pts.topRight.isTop = true; - pts.bottomLeft.isBottom = true; - pts.bottomRight.isBottom = true; + if (badEnd || badAEnd || closeEndACp) { + overlapping = true; - return pts; - }, + // project control point along line from tgt centre to outside the tgt shape + // (otherwise intersection will yield nothing) + var cpD = { // delta + x: rs.ctrlpts[0] - tgtPos.x, + y: rs.ctrlpts[1] - tgtPos.y + }; + var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line + var cpM = { // normalised delta + x: cpD.x / cpL, + y: cpD.y / cpL + }; + var radius = Math.max(srcW, srcH); + var cpProj = { // *2 radius guarantees outside shape + x: rs.ctrlpts[0] + cpM.x * 2 * radius, + y: rs.ctrlpts[1] + cpM.y * 2 * radius + }; - checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { + var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, cpProj.x, cpProj.y, 0); - var curveConstants = math.getBarrelCurveConstants(width, height); - var hOffset = curveConstants.heightOffset; - var wOffset = curveConstants.widthOffset; + if (closeEndACp) { + rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - endACpDist); + rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - endACpDist); + } else { + rs.ctrlpts[0] = tgtCtrlPtIntn[0] + cpM.x * minCpADist; + rs.ctrlpts[1] = tgtCtrlPtIntn[1] + cpM.y * minCpADist; + } + } - // Check hBox - if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) { - return true; - } + if (overlapping) { + // recalc endpts + this.findEndpoints(edge); + } + } - // Check vBox - if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) { - return true; - } + if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') { + rs.allpts = []; - var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY); + rs.allpts.push(rs.startX, rs.startY); - var getCurveT = function getCurveT(x, y, curvePts) { - var x0 = curvePts[4]; - var x1 = curvePts[2]; - var x2 = curvePts[0]; - var y0 = curvePts[5]; - // var y1 = curvePts[ 3 ]; - var y2 = curvePts[1]; + for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) { + // ctrl pt itself + rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); - var xMin = Math.min(x0, x2); - var xMax = Math.max(x0, x2); - var yMin = Math.min(y0, y2); - var yMax = Math.max(y0, y2); + // the midpt between ctrlpts as intermediate destination pts + if (b + 3 < rs.ctrlpts.length) { + rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2); + } + } - if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) { - var coeff = math.bezierPtsToQuadCoeff(x0, x1, x2); - var roots = math.solveQuadratic(coeff[0], coeff[1], coeff[2], x); + rs.allpts.push(rs.endX, rs.endY); - var validRoots = roots.filter(function (r) { - return 0 <= r && r <= 1; - }); + var m, mt; + if (rs.ctrlpts.length / 2 % 2 === 0) { + m = rs.allpts.length / 2 - 1; - if (validRoots.length > 0) { - return validRoots[0]; + rs.midX = rs.allpts[m]; + rs.midY = rs.allpts[m + 1]; + } else { + m = rs.allpts.length / 2 - 3; + mt = 0.5; + + rs.midX = math.qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt); + rs.midY = math.qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt); } - } - return null; - }; + } else if (rs.edgeType === 'straight') { + // need to calc these after endpts + rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; - var curveRegions = Object.keys(barrelCurvePts); - for (var i = 0; i < curveRegions.length; i++) { - var corner = curveRegions[i]; - var cornerPts = barrelCurvePts[corner]; - var t = getCurveT(x, y, cornerPts); + // default midpt for labels etc + rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4; + rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4; + } else if (rs.edgeType === 'segments') { + rs.allpts = []; + rs.allpts.push(rs.startX, rs.startY); + rs.allpts.push.apply(rs.allpts, rs.segpts); + rs.allpts.push(rs.endX, rs.endY); - if (t == null) { - continue; - } + if (rs.segpts.length % 4 === 0) { + var i2 = rs.segpts.length / 2; + var i1 = i2 - 2; - var y0 = cornerPts[5]; - var y1 = cornerPts[3]; - var y2 = cornerPts[1]; - var bezY = math.qbezierAt(y0, y1, y2, t); + rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2; + rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2; + } else { + var i1 = rs.segpts.length / 2 - 1; - if (cornerPts.isTop && bezY <= y) { - return true; - } - if (cornerPts.isBottom && y <= bezY) { - return true; + rs.midX = rs.segpts[i1]; + rs.midY = rs.segpts[i1 + 1]; + } } - } - return false; - } - }; -}; - -BRp.generateBottomRoundrectangle = function () { - return this.nodeShapes['bottomroundrectangle'] = { - renderer: this, - name: 'bottomroundrectangle', + this.storeEdgeProjections(edge); + this.calculateArrowAngles(edge); + } // if point cache miss - points: math.generateUnitNgonPointsFitToSquare(4, 0), + this.recalculateEdgeLabelProjections(edge); + this.calculateLabelAngles(edge); + } // for pair edges + } // for pair ids - draw: function draw(context, centerX, centerY, width, height) { - this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); - }, + for (var i = 0; i < haystackEdges.length; i++) { + var edge = haystackEdges[i]; + var _p = edge._private; + var rscratch = _p.rscratch; + var rs = rscratch; - intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { - var topStartX = nodeX - (width / 2 + padding); - var topStartY = nodeY - (height / 2 + padding); - var topEndY = topStartY; - var topEndX = nodeX + (width / 2 + padding); + if (!rscratch.haystack) { + var angle = Math.random() * 2 * Math.PI; - var topIntersections = math.finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false); - if (topIntersections.length > 0) { - return topIntersections; - } + rscratch.source = { + x: Math.cos(angle), + y: Math.sin(angle) + }; - return math.roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding); - }, + var angle = Math.random() * 2 * Math.PI; - checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { + rscratch.target = { + x: Math.cos(angle), + y: Math.sin(angle) + }; + } - var cornerRadius = math.getRoundRectangleRadius(width, height); - var diam = 2 * cornerRadius; + var src = _p.source; + var tgt = _p.target; + var srcPos = src.position(); + var tgtPos = tgt.position(); + var srcW = src.width(); + var tgtW = tgt.width(); + var srcH = src.height(); + var tgtH = tgt.height(); + var radius = edge.pstyle('haystack-radius').value; + var halfRadius = radius / 2; // b/c have to half width/height - // Check hBox - if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) { - return true; - } + rs.haystackPts = rs.allpts = [rs.source.x * srcW * halfRadius + srcPos.x, rs.source.y * srcH * halfRadius + srcPos.y, rs.target.x * tgtW * halfRadius + tgtPos.x, rs.target.y * tgtH * halfRadius + tgtPos.y]; - // Check vBox - if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) { - return true; - } + rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2; + rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; - // check non-rounded top side - var outerWidth = width / 2 + 2 * padding; - var outerHeight = height / 2 + 2 * padding; - var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight]; - if (math.pointInsidePolygonPoints(x, y, points)) { - return true; - } + // always override as haystack in case set to different type previously + rscratch.edgeType = rscratch.lastCurveStyle = 'haystack'; + rscratch.haystack = true; - // Check bottom right quarter circle - if (math.checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) { + this.storeEdgeProjections(edge); + this.calculateArrowAngles(edge); + this.recalculateEdgeLabelProjections(edge); + this.calculateLabelAngles(edge); + } +}; - return true; - } +function getPts(pts) { + var retPts = []; - // Check bottom left quarter circle - if (math.checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) { + if (pts == null) { + return; + } - return true; - } + for (var i = 0; i < pts.length; i += 2) { + var x = pts[i]; + var y = pts[i + 1]; - return false; - } - }; -}; + retPts.push({ x: x, y: y }); + } -BRp.registerNodeShapes = function () { - var nodeShapes = this.nodeShapes = {}; - var renderer = this; + return retPts; +} - this.generateEllipse(); +BRp.getSegmentPoints = function (edge) { + var rs = edge[0]._private.rscratch; + var type = rs.edgeType; - this.generatePolygon('triangle', math.generateUnitNgonPointsFitToSquare(3, 0)); + if (type === 'segments') { + return getPts(rs.segpts); + } +}; - this.generatePolygon('rectangle', math.generateUnitNgonPointsFitToSquare(4, 0)); - nodeShapes['square'] = nodeShapes['rectangle']; +BRp.getControlPoints = function (edge) { + var rs = edge[0]._private.rscratch; + var type = rs.edgeType; - this.generateRoundRectangle(); + if (type === 'bezier' || type === 'multibezier') { + return getPts(rs.ctrlpts); + } +}; - this.generateCutRectangle(); +BRp.getEdgeMidpoint = function (edge) { + var rs = edge[0]._private.rscratch; - this.generateBarrel(); + return { + x: rs.midX, + y: rs.midY + }; +}; - this.generateBottomRoundrectangle(); +module.exports = BRp; - this.generatePolygon('diamond', [0, 1, 1, 0, 0, -1, -1, 0]); +/***/ }), +/* 113 */ +/***/ (function(module, exports, __webpack_require__) { - this.generatePolygon('pentagon', math.generateUnitNgonPointsFitToSquare(5, 0)); +"use strict"; - this.generatePolygon('hexagon', math.generateUnitNgonPointsFitToSquare(6, 0)); - this.generatePolygon('heptagon', math.generateUnitNgonPointsFitToSquare(7, 0)); +var math = __webpack_require__(2); +var is = __webpack_require__(0); - this.generatePolygon('octagon', math.generateUnitNgonPointsFitToSquare(8, 0)); +var BRp = {}; - var star5Points = new Array(20); - { - var outerPoints = math.generateUnitNgonPoints(5, 0); - var innerPoints = math.generateUnitNgonPoints(5, Math.PI / 5); +BRp.manualEndptToPx = function (node, prop) { + var r = this; + var npos = node.position(); + var w = node.outerWidth(); + var h = node.outerHeight(); - // Outer radius is 1; inner radius of star is smaller - var innerRadius = 0.5 * (3 - Math.sqrt(5)); - innerRadius *= 1.57; + if (prop.value.length === 2) { + var p = [prop.pfValue[0], prop.pfValue[1]]; - for (var i = 0; i < innerPoints.length / 2; i++) { - innerPoints[i * 2] *= innerRadius; - innerPoints[i * 2 + 1] *= innerRadius; + if (prop.units[0] === '%') { + p[0] = p[0] * w; } - for (var i = 0; i < 20 / 4; i++) { - star5Points[i * 4] = outerPoints[i * 2]; - star5Points[i * 4 + 1] = outerPoints[i * 2 + 1]; - - star5Points[i * 4 + 2] = innerPoints[i * 2]; - star5Points[i * 4 + 3] = innerPoints[i * 2 + 1]; + if (prop.units[1] === '%') { + p[1] = p[1] * h; } - } - star5Points = math.fitPolygonToSquare(star5Points); + p[0] += npos.x; + p[1] += npos.y; - this.generatePolygon('star', star5Points); + return p; + } else { + var angle = prop.pfValue[0]; - this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]); + angle = -Math.PI / 2 + angle; // start at 12 o'clock - this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]); + var l = 2 * Math.max(w, h); - this.generatePolygon('concavehexagon', [-1, -0.95, -0.75, 0, -1, 0.95, 1, 0.95, 0.75, 0, 1, -0.95]); + var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l]; - this.generatePolygon('tag', [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1]); + return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0); + } +}; - nodeShapes.makePolygon = function (points) { +BRp.findEndpoints = function (edge) { + var r = this; + var intersect = void 0; - // use caching on user-specified polygons so they are as fast as native shapes + var source = edge.source()[0]; + var target = edge.target()[0]; - var key = points.join('$'); - var name = 'polygon-' + key; - var shape; + var srcPos = source.position(); + var tgtPos = target.position(); - if (shape = this[name]) { - // got cached shape - return shape; - } + var tgtArShape = edge.pstyle('target-arrow-shape').value; + var srcArShape = edge.pstyle('source-arrow-shape').value; - // create and cache new shape - return renderer.generatePolygon(name, points); - }; -}; + var tgtDist = edge.pstyle('target-distance-from-node').pfValue; + var srcDist = edge.pstyle('source-distance-from-node').pfValue; -module.exports = BRp; + var rs = edge._private.rscratch; -/***/ }), -/* 100 */ -/***/ (function(module, exports, __webpack_require__) { + var et = rs.edgeType; + var self = et === 'self' || et === 'compound'; + var bezier = et === 'bezier' || et === 'multibezier' || self; + var multi = et !== 'bezier'; + var lines = et === 'straight' || et === 'segments'; + var segments = et === 'segments'; + var hasEndpts = bezier || multi || lines; + var srcManEndpt = edge.pstyle('source-endpoint'); + var srcManEndptVal = self ? 'outside-to-node' : srcManEndpt.value; + var tgtManEndpt = edge.pstyle('target-endpoint'); + var tgtManEndptVal = self ? 'outside-to-node' : tgtManEndpt.value; -"use strict"; + rs.srcManEndpt = srcManEndpt; + rs.tgtManEndpt = tgtManEndpt; + var p1 = void 0; // last known point of edge on target side + var p2 = void 0; // last known point of edge on source side -var util = __webpack_require__(1); + var p1_i = void 0; // point to intersect with target shape + var p2_i = void 0; // point to intersect with source shape -var BRp = {}; + if (bezier) { + var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]]; + var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart; -BRp.timeToRender = function () { - return this.redrawTotalTime / this.redrawCount; -}; + p1 = cpEnd; + p2 = cpStart; + } else if (lines) { + var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2); + var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2); -BRp.redraw = function (options) { - options = options || util.staticEmptyObject(); + p1 = tgtArrowFromPt; + p2 = srcArrowFromPt; + } - var r = this; + if (tgtManEndptVal === 'inside-to-node') { + intersect = [tgtPos.x, tgtPos.y]; + } else if (tgtManEndpt.units) { + intersect = this.manualEndptToPx(target, tgtManEndpt); + } else if (tgtManEndptVal === 'outside-to-line') { + intersect = rs.tgtIntn; // use cached value from ctrlpt calc + } else { + if (tgtManEndptVal === 'outside-to-node') { + p1_i = p1; + } else if (tgtManEndptVal === 'outside-to-line') { + p1_i = [srcPos.x, srcPos.y]; + } - if (r.averageRedrawTime === undefined) { - r.averageRedrawTime = 0; - } - if (r.lastRedrawTime === undefined) { - r.lastRedrawTime = 0; - } - if (r.lastDrawTime === undefined) { - r.lastDrawTime = 0; + intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0); } - r.requestedFrame = true; - r.renderOptions = options; -}; + var arrowEnd = math.shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist); + var edgeEnd = math.shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist); -BRp.beforeRender = function (fn, priority) { - // the renderer can't add tick callbacks when destroyed - if (this.destroyed) { - return; + rs.endX = edgeEnd[0]; + rs.endY = edgeEnd[1]; + + rs.arrowEndX = arrowEnd[0]; + rs.arrowEndY = arrowEnd[1]; + + if (srcManEndptVal === 'inside-to-node') { + intersect = [srcPos.x, srcPos.y]; + } else if (srcManEndpt.units) { + intersect = this.manualEndptToPx(source, srcManEndpt); + } else if (srcManEndptVal === 'outside-to-line') { + intersect = rs.srcIntn; // use cached value from ctrlpt calc + } else { + if (srcManEndptVal === 'outside-to-node') { + p2_i = p2; + } else if (srcManEndptVal === 'outside-to-line') { + p2_i = [tgtPos.x, tgtPos.y]; + } + + intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0); } - priority = priority || 0; + var arrowStart = math.shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist); + var edgeStart = math.shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist); - var cbs = this.beforeRenderCallbacks; + rs.startX = edgeStart[0]; + rs.startY = edgeStart[1]; - cbs.push({ fn: fn, priority: priority }); + rs.arrowStartX = arrowStart[0]; + rs.arrowStartY = arrowStart[1]; - // higher priority callbacks executed first - cbs.sort(function (a, b) { - return b.priority - a.priority; - }); + if (hasEndpts) { + if (!is.number(rs.startX) || !is.number(rs.startY) || !is.number(rs.endX) || !is.number(rs.endY)) { + rs.badLine = true; + } else { + rs.badLine = false; + } + } }; -var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) { - var cbs = r.beforeRenderCallbacks; +BRp.getSourceEndpoint = function (edge) { + var rs = edge[0]._private.rscratch; - for (var i = 0; i < cbs.length; i++) { - cbs[i].fn(willDraw, startTime); + switch (rs.edgeType) { + case 'haystack': + return { + x: rs.haystackPts[0], + y: rs.haystackPts[1] + }; + default: + return { + x: rs.arrowStartX, + y: rs.arrowStartY + }; } }; -BRp.startRenderLoop = function () { - var r = this; +BRp.getTargetEndpoint = function (edge) { + var rs = edge[0]._private.rscratch; - if (r.renderLoopStarted) { - return; - } else { - r.renderLoopStarted = true; + switch (rs.edgeType) { + case 'haystack': + return { + x: rs.haystackPts[2], + y: rs.haystackPts[3] + }; + default: + return { + x: rs.arrowEndX, + y: rs.arrowEndY + }; } +}; + +module.exports = BRp; - var renderFn = function renderFn(requestTime) { - if (r.destroyed) { - return; - } +/***/ }), +/* 114 */ +/***/ (function(module, exports, __webpack_require__) { - if (r.requestedFrame && !r.skipFrame) { - beforeRenderCallbacks(r, true, requestTime); +"use strict"; - var startTime = util.performanceNow(); - r.render(r.renderOptions); +var math = __webpack_require__(2); - var endTime = r.lastDrawTime = util.performanceNow(); +var BRp = {}; - if (r.averageRedrawTime === undefined) { - r.averageRedrawTime = endTime - startTime; - } +function pushBezierPts(r, edge, pts) { + var qbezierAt = function qbezierAt(p1, p2, p3, t) { + return math.qbezierAt(p1, p2, p3, t); + }; + var _p = edge._private; + var bpts = _p.rstyle.bezierPts; - if (r.redrawCount === undefined) { - r.redrawCount = 0; - } + for (var i = 0; i < r.bezierProjPcts.length; i++) { + var p = r.bezierProjPcts[i]; - r.redrawCount++; + bpts.push({ + x: qbezierAt(pts[0], pts[2], pts[4], p), + y: qbezierAt(pts[1], pts[3], pts[5], p) + }); + } +} - if (r.redrawTotalTime === undefined) { - r.redrawTotalTime = 0; - } +BRp.storeEdgeProjections = function (edge) { + var _p = edge._private; + var rs = _p.rscratch; + var et = rs.edgeType; - var duration = endTime - startTime; + // clear the cached points state + _p.rstyle.bezierPts = null; + _p.rstyle.linePts = null; + _p.rstyle.haystackPts = null; - r.redrawTotalTime += duration; - r.lastRedrawTime = duration; + if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') { + var bpts = _p.rstyle.bezierPts = []; // jshint ignore:line - // use a weighted average with a bias from the previous average so we don't spike so easily - r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2; + for (var i = 0; i + 5 < rs.allpts.length; i += 4) { + pushBezierPts(this, edge, rs.allpts.slice(i, i + 6)); + } + } else if (et === 'segments') { + var lpts = _p.rstyle.linePts = []; - r.requestedFrame = false; - } else { - beforeRenderCallbacks(r, false, requestTime); + for (var i = 0; i + 1 < rs.allpts.length; i += 2) { + lpts.push({ + x: rs.allpts[i], + y: rs.allpts[i + 1] + }); } + } else if (et === 'haystack') { + var hpts = rs.haystackPts; - r.skipFrame = false; + _p.rstyle.haystackPts = [{ x: hpts[0], y: hpts[1] }, { x: hpts[2], y: hpts[3] }]; + } - util.requestAnimationFrame(renderFn); - }; + _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth; +}; - util.requestAnimationFrame(renderFn); +BRp.recalculateEdgeProjections = function (edges) { + this.findEdgeControlPoints(edges); }; module.exports = BRp; /***/ }), -/* 101 */ +/* 115 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var CRp = {}; +var math = __webpack_require__(2); +var is = __webpack_require__(0); +var util = __webpack_require__(1); -var impl; +var BRp = {}; -CRp.arrowShapeImpl = function (name) { - return (impl || (impl = { - 'polygon': function polygon(context, points) { - for (var i = 0; i < points.length; i++) { - var pt = points[i]; +BRp.recalculateNodeLabelProjection = function (node) { + var content = node.pstyle('label').strValue; - context.lineTo(pt.x, pt.y); - } - }, + if (is.emptyString(content)) { + return; + } - 'triangle-backcurve': function triangleBackcurve(context, points, controlPoint) { - var firstPt; + var textX, textY; + var _p = node._private; + var nodeWidth = node.width(); + var nodeHeight = node.height(); + var padding = node.padding(); + var nodePos = node.position(); + var textHalign = node.pstyle('text-halign').strValue; + var textValign = node.pstyle('text-valign').strValue; + var rs = _p.rscratch; + var rstyle = _p.rstyle; - for (var i = 0; i < points.length; i++) { - var pt = points[i]; + switch (textHalign) { + case 'left': + textX = nodePos.x - nodeWidth / 2 - padding; + break; - if (i === 0) { - firstPt = pt; - } + case 'right': + textX = nodePos.x + nodeWidth / 2 + padding; + break; - context.lineTo(pt.x, pt.y); - } + default: + // e.g. center + textX = nodePos.x; + } - context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y); - }, + switch (textValign) { + case 'top': + textY = nodePos.y - nodeHeight / 2 - padding; + break; - 'triangle-tee': function triangleTee(context, trianglePoints, teePoints) { - if (context.beginPath) { - context.beginPath(); - } + case 'bottom': + textY = nodePos.y + nodeHeight / 2 + padding; + break; - var triPts = trianglePoints; - for (var i = 0; i < triPts.length; i++) { - var pt = triPts[i]; + default: + // e.g. middle + textY = nodePos.y; + } - context.lineTo(pt.x, pt.y); - } + rs.labelX = textX; + rs.labelY = textY; + rstyle.labelX = textX; + rstyle.labelY = textY; - if (context.closePath) { - context.closePath(); - } + this.applyLabelDimensions(node); +}; - if (context.beginPath) { - context.beginPath(); - } +BRp.recalculateEdgeLabelProjections = function (edge) { + var p; + var _p = edge._private; + var rs = _p.rscratch; + var r = this; + var content = { + mid: edge.pstyle('label').strValue, + source: edge.pstyle('source-label').strValue, + target: edge.pstyle('target-label').strValue + }; - var teePts = teePoints; - var firstTeePt = teePoints[0]; - context.moveTo(firstTeePt.x, firstTeePt.y); + if (content.mid || content.source || content.target) { + // then we have to calculate... + } else { + return; // no labels => no calcs + } - for (var i = 0; i < teePts.length; i++) { - var pt = teePts[i]; + // add center point to style so bounding box calculations can use it + // + p = { + x: rs.midX, + y: rs.midY + }; - context.lineTo(pt.x, pt.y); - } - if (context.closePath) { - context.closePath(); - } - }, + var setRs = function setRs(propName, prefix, value) { + util.setPrefixedProperty(_p.rscratch, propName, prefix, value); + util.setPrefixedProperty(_p.rstyle, propName, prefix, value); + }; - 'triangle-cross': function triangleCross(context, trianglePoints, crossLinePoints) { - if (context.beginPath) { - context.beginPath(); - } + setRs('labelX', null, p.x); + setRs('labelY', null, p.y); - var triPts = trianglePoints; - for (var i = 0; i < triPts.length; i++) { - var pt = triPts[i]; + var createControlPointInfo = function createControlPointInfo() { + if (createControlPointInfo.cache) { + return createControlPointInfo.cache; + } // use cache so only 1x per edge - context.lineTo(pt.x, pt.y); - } + var ctrlpts = []; - if (context.closePath) { - context.closePath(); - } + // store each ctrlpt info init + for (var i = 0; i + 5 < rs.allpts.length; i += 4) { + var p0 = { x: rs.allpts[i], y: rs.allpts[i + 1] }; + var p1 = { x: rs.allpts[i + 2], y: rs.allpts[i + 3] }; // ctrlpt + var p2 = { x: rs.allpts[i + 4], y: rs.allpts[i + 5] }; + + ctrlpts.push({ + p0: p0, + p1: p1, + p2: p2, + startDist: 0, + length: 0, + segments: [] + }); + } + + var bpts = _p.rstyle.bezierPts; + var nProjs = r.bezierProjPcts.length; + + function addSegment(cp, p0, p1, t0, t1) { + var length = math.dist(p0, p1); + var prevSegment = cp.segments[cp.segments.length - 1]; + var segment = { + p0: p0, + p1: p1, + t0: t0, + t1: t1, + startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0, + length: length + }; + + cp.segments.push(segment); + + cp.length += length; + } + + // update each ctrlpt with segment info + for (var i = 0; i < ctrlpts.length; i++) { + var cp = ctrlpts[i]; + var prevCp = ctrlpts[i - 1]; - if (context.beginPath) { - context.beginPath(); + if (prevCp) { + cp.startDist = prevCp.startDist + prevCp.length; } - var teePts = crossLinePoints; - var firstTeePt = crossLinePoints[0]; - context.moveTo(firstTeePt.x, firstTeePt.y); - - for (var i = 0; i < teePts.length; i++) { - var pt = teePts[i]; + addSegment(cp, cp.p0, bpts[i * nProjs], 0, r.bezierProjPcts[0]); // first - context.lineTo(pt.x, pt.y); - } - if (context.closePath) { - context.closePath(); + for (var j = 0; j < nProjs - 1; j++) { + addSegment(cp, bpts[i * nProjs + j], bpts[i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]); } - }, - 'circle': function circle(context, rx, ry, r) { - context.arc(rx, ry, r, 0, Math.PI * 2, false); + addSegment(cp, bpts[i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last } - }))[name]; -}; - -module.exports = CRp; - -/***/ }), -/* 102 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; + return createControlPointInfo.cache = ctrlpts; + }; -var CRp = {}; + var calculateEndProjection = function calculateEndProjection(prefix) { + var angle; + var isSrc = prefix === 'source'; -CRp.drawEdge = function (context, edge, shiftToOriginWithBb, drawLabel) { - var r = this; - var rs = edge._private.rscratch; - var usePaths = r.usePaths(); + if (!content[prefix]) { + return; + } - // if bezier ctrl pts can not be calculated, then die - if (rs.badLine || isNaN(rs.allpts[0])) { - // isNaN in case edge is impossible and browser bugs (e.g. safari) - return; - } + var offset = edge.pstyle(prefix + '-text-offset').pfValue; - if (!edge.visible()) { - return; - } + var lineAngle = function lineAngle(p0, p1) { + var dx = p1.x - p0.x; + var dy = p1.y - p0.y; - var bb = void 0; - if (shiftToOriginWithBb) { - bb = shiftToOriginWithBb; + return Math.atan(dy / dx); + }; - context.translate(-bb.x1, -bb.y1); - } + var bezierAngle = function bezierAngle(p0, p1, p2, t) { + var t0 = math.bound(0, t - 0.001, 1); + var t1 = math.bound(0, t + 0.001, 1); - var overlayPadding = edge.pstyle('overlay-padding').pfValue; - var overlayWidth = 2 * overlayPadding; - var overlayOpacity = edge.pstyle('overlay-opacity').value; - var overlayColor = edge.pstyle('overlay-color').value; - var lineColor = edge.pstyle('line-color').value; - var opacity = edge.pstyle('opacity').value; - var lineStyle = edge.pstyle('line-style').value; - var edgeWidth = edge.pstyle('width').pfValue; + var lp0 = math.qbezierPtAt(p0, p1, p2, t0); + var lp1 = math.qbezierPtAt(p0, p1, p2, t1); - var drawLine = function drawLine() { - var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity; + return lineAngle(lp0, lp1); + }; - context.lineWidth = edgeWidth; - context.lineCap = 'butt'; + switch (rs.edgeType) { + case 'self': + case 'compound': + case 'bezier': + case 'multibezier': + var cps = createControlPointInfo(); + var selected; + var startDist = 0; + var totalDist = 0; - r.strokeStyle(context, lineColor[0], lineColor[1], lineColor[2], strokeOpacity); + // find the segment we're on + for (var i = 0; i < cps.length; i++) { + var cp = cps[isSrc ? i : cps.length - 1 - i]; - r.drawEdgePath(edge, context, rs.allpts, lineStyle); - }; + for (var j = 0; j < cp.segments.length; j++) { + var seg = cp.segments[isSrc ? j : cp.segments.length - 1 - j]; + var lastSeg = i === cps.length - 1 && j === cp.segments.length - 1; - var drawOverlay = function drawOverlay() { - var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overlayOpacity; + startDist = totalDist; + totalDist += seg.length; - context.lineWidth = overlayWidth; + if (totalDist >= offset || lastSeg) { + selected = { cp: cp, segment: seg }; + break; + } + } - if (rs.edgeType === 'self' && !usePaths) { - context.lineCap = 'butt'; - } else { - context.lineCap = 'round'; - } + if (selected) { + break; + } + } - r.strokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], strokeOpacity); + var cp = selected.cp; + var seg = selected.segment; + var tSegment = (offset - startDist) / seg.length; + var segDt = seg.t1 - seg.t0; + var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment; - r.drawEdgePath(edge, context, rs.allpts, 'solid'); - }; + t = math.bound(0, t, 1); + p = math.qbezierPtAt(cp.p0, cp.p1, cp.p2, t); + angle = bezierAngle(cp.p0, cp.p1, cp.p2, t, p); - var drawArrows = function drawArrows() { - var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity; + break; - r.drawArrowheads(context, edge, arrowOpacity); - }; + case 'straight': + case 'segments': + case 'haystack': + var d = 0, + di, + d0; + var p0, p1; + var l = rs.allpts.length; - var drawText = function drawText() { - r.drawElementText(context, edge, drawLabel); - }; + for (var i = 0; i + 3 < l; i += 2) { + if (isSrc) { + p0 = { x: rs.allpts[i], y: rs.allpts[i + 1] }; + p1 = { x: rs.allpts[i + 2], y: rs.allpts[i + 3] }; + } else { + p0 = { x: rs.allpts[l - 2 - i], y: rs.allpts[l - 1 - i] }; + p1 = { x: rs.allpts[l - 4 - i], y: rs.allpts[l - 3 - i] }; + } - context.lineJoin = 'round'; + di = math.dist(p0, p1); + d0 = d; + d += di; - var ghost = edge.pstyle('ghost').value === 'yes'; + if (d >= offset) { + break; + } + } - if (ghost) { - var gx = edge.pstyle('ghost-offset-x').pfValue; - var gy = edge.pstyle('ghost-offset-y').pfValue; - var ghostOpacity = edge.pstyle('ghost-opacity').value; - var effectiveGhostOpacity = opacity * ghostOpacity; + var pD = offset - d0; + var t = pD / di; - context.translate(gx, gy); + t = math.bound(0, t, 1); + p = math.lineAt(p0, p1, t); + angle = lineAngle(p0, p1); - drawLine(effectiveGhostOpacity); - drawArrows(effectiveGhostOpacity); + break; + } - context.translate(-gx, -gy); - } + setRs('labelX', prefix, p.x); + setRs('labelY', prefix, p.y); + setRs('labelAutoAngle', prefix, angle); + }; - drawLine(); - drawArrows(); - drawOverlay(); - drawText(); + calculateEndProjection('source'); + calculateEndProjection('target'); - if (shiftToOriginWithBb) { - context.translate(bb.x1, bb.y1); - } + this.applyLabelDimensions(edge); }; -CRp.drawEdgePath = function (edge, context, pts, type) { - var rs = edge._private.rscratch; - var canvasCxt = context; - var path = void 0; - var pathCacheHit = false; - var usePaths = this.usePaths(); - - if (usePaths) { - var pathCacheKey = pts.join('$'); - var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey; +BRp.applyLabelDimensions = function (ele) { + this.applyPrefixedLabelDimensions(ele); - if (keyMatches) { - path = context = rs.pathCache; - pathCacheHit = true; - } else { - path = context = new Path2D(); // eslint-disable-line no-undef - rs.pathCacheKey = pathCacheKey; - rs.pathCache = path; - } + if (ele.isEdge()) { + this.applyPrefixedLabelDimensions(ele, 'source'); + this.applyPrefixedLabelDimensions(ele, 'target'); } +}; - if (canvasCxt.setLineDash) { - // for very outofdate browsers - switch (type) { - case 'dotted': - canvasCxt.setLineDash([1, 1]); - break; - - case 'dashed': - canvasCxt.setLineDash([6, 3]); - break; +BRp.applyPrefixedLabelDimensions = function (ele, prefix) { + var _p = ele._private; - case 'solid': - canvasCxt.setLineDash([]); - break; - } - } + var text = this.getLabelText(ele, prefix); + var labelDims = this.calculateLabelDimensions(ele, text); - if (!pathCacheHit && !rs.badLine) { - if (context.beginPath) { - context.beginPath(); - } - context.moveTo(pts[0], pts[1]); + util.setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, labelDims.width); + util.setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, labelDims.width); - switch (rs.edgeType) { - case 'bezier': - case 'self': - case 'compound': - case 'multibezier': - for (var i = 2; i + 3 < pts.length; i += 4) { - context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]); - } - break; + util.setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, labelDims.height); + util.setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, labelDims.height); +}; - case 'straight': - case 'segments': - case 'haystack': - for (var _i = 2; _i + 1 < pts.length; _i += 2) { - context.lineTo(pts[_i], pts[_i + 1]); - } - break; +BRp.getLabelText = function (ele, prefix) { + var _p = ele._private; + var pfd = prefix ? prefix + '-' : ''; + var text = ele.pstyle(pfd + 'label').strValue; + var textTransform = ele.pstyle('text-transform').value; + var rscratch = function rscratch(propName, value) { + if (value) { + util.setPrefixedProperty(_p.rscratch, propName, prefix, value); + return value; + } else { + return util.getPrefixedProperty(_p.rscratch, propName, prefix); } - } + }; - context = canvasCxt; - if (usePaths) { - context.stroke(path); - } else { - context.stroke(); + if (textTransform == 'none') { + // passthrough + } else if (textTransform == 'uppercase') { + text = text.toUpperCase(); + } else if (textTransform == 'lowercase') { + text = text.toLowerCase(); } - // reset any line dashes - if (context.setLineDash) { - // for very outofdate browsers - context.setLineDash([]); - } -}; + var wrapStyle = ele.pstyle('text-wrap').value; -CRp.drawArrowheads = function (context, edge, opacity) { - var rs = edge._private.rscratch; - var isHaystack = rs.edgeType === 'haystack'; + if (wrapStyle === 'wrap') { + //console.log('wrap'); - if (!isHaystack) { - this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity); - } + var labelKey = rscratch('labelKey'); - this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity); + // save recalc if the label is the same as before + if (labelKey && rscratch('labelWrapKey') === labelKey) { + // console.log('wrap cache hit'); + return rscratch('labelWrapCachedText'); + } + // console.log('wrap cache miss'); - this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity); + var lines = text.split('\n'); + var maxW = ele.pstyle('text-max-width').pfValue; + var wrappedLines = []; - if (!isHaystack) { - this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity); - } -}; + for (var l = 0; l < lines.length; l++) { + var line = lines[l]; + var lineDims = this.calculateLabelDimensions(ele, line, 'line=' + line); + var lineW = lineDims.width; -CRp.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) { - if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) { - return; - } + if (lineW > maxW) { + // line is too long + var words = line.split(/\s+/); // NB: assume collapsed whitespace into single space + var subline = ''; - var self = this; - var arrowShape = edge.pstyle(prefix + '-arrow-shape').value; - if (arrowShape === 'none') { - return; - } + for (var w = 0; w < words.length; w++) { + var word = words[w]; + var testLine = subline.length === 0 ? word : subline + ' ' + word; + var testDims = this.calculateLabelDimensions(ele, testLine, 'testLine=' + testLine); + var testW = testDims.width; - var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled'; - var arrowFill = edge.pstyle(prefix + '-arrow-fill').value; - var edgeWidth = edge.pstyle('width').pfValue; - var edgeOpacity = edge.pstyle('opacity').value; + if (testW <= maxW) { + // word fits on current line + subline += word + ' '; + } else { + // word starts new line + wrappedLines.push(subline); + subline = word + ' '; + } + } - if (opacity === undefined) { - opacity = edgeOpacity; - } + // if there's remaining text, put it in a wrapped line + if (!subline.match(/^\s+$/)) { + wrappedLines.push(subline); + } + } else { + // line is already short enough + wrappedLines.push(line); + } + } // for - var gco = context.globalCompositeOperation; + rscratch('labelWrapCachedLines', wrappedLines); + text = rscratch('labelWrapCachedText', wrappedLines.join('\n')); + rscratch('labelWrapKey', labelKey); - if (opacity !== 1 || arrowFill === 'hollow') { - // then extra clear is needed - context.globalCompositeOperation = 'destination-out'; + // console.log(text) + } else if (wrapStyle === 'ellipsis') { + var maxW = ele.pstyle('text-max-width').pfValue; + var ellipsized = ''; + var ellipsis = '\u2026'; + var incLastCh = false; - self.fillStyle(context, 255, 255, 255, 1); - self.strokeStyle(context, 255, 255, 255, 1); + for (var i = 0; i < text.length; i++) { + var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width; - self.drawArrowShape(edge, prefix, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle); + if (widthWithNextCh > maxW) { + break; + } - context.globalCompositeOperation = gco; - } // otherwise, the opaque arrow clears it for free :) + ellipsized += text[i]; - var color = edge.pstyle(prefix + '-arrow-color').value; - self.fillStyle(context, color[0], color[1], color[2], opacity); - self.strokeStyle(context, color[0], color[1], color[2], opacity); + if (i === text.length - 1) { + incLastCh = true; + } + } - self.drawArrowShape(edge, prefix, context, arrowFill, edgeWidth, arrowShape, x, y, angle); + if (!incLastCh) { + ellipsized += ellipsis; + } + + return ellipsized; + } // if ellipsize + + return text; }; -CRp.drawArrowShape = function (edge, arrowType, context, fill, edgeWidth, shape, x, y, angle) { +BRp.calculateLabelDimensions = function (ele, text, extraKey) { var r = this; - var usePaths = this.usePaths(); - var rs = edge._private.rscratch; - var pathCacheHit = false; - var path = void 0; - var canvasContext = context; - var translation = { x: x, y: y }; - var scale = edge.pstyle('arrow-scale').value; - var size = this.getArrowWidth(edgeWidth, scale); - var shapeImpl = r.arrowShapes[shape]; - if (usePaths) { - var pathCacheKey = size + '$' + shape + '$' + angle + '$' + x + '$' + y; - rs.arrowPathCacheKey = rs.arrowPathCacheKey || {}; - rs.arrowPathCache = rs.arrowPathCache || {}; + var cacheKey = ele._private.labelStyleKey + '$@$' + text; - var alreadyCached = rs.arrowPathCacheKey[arrowType] === pathCacheKey; - if (alreadyCached) { - path = context = rs.arrowPathCache[arrowType]; - pathCacheHit = true; - } else { - path = context = new Path2D(); // eslint-disable-line no-undef - rs.arrowPathCacheKey[arrowType] = pathCacheKey; - rs.arrowPathCache[arrowType] = path; - } + if (extraKey) { + cacheKey += '$@$' + extraKey; } - if (context.beginPath) { - context.beginPath(); - } + var cache = r.labelDimCache || (r.labelDimCache = {}); - if (!pathCacheHit) { - shapeImpl.draw(context, size, angle, translation, edgeWidth); + if (cache[cacheKey]) { + return cache[cacheKey]; } - if (!shapeImpl.leavePathOpen && context.closePath) { - context.closePath(); + var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text + var fStyle = ele.pstyle('font-style').strValue; + var size = sizeMult * ele.pstyle('font-size').pfValue + 'px'; + var family = ele.pstyle('font-family').strValue; + var weight = ele.pstyle('font-weight').strValue; + + var div = this.labelCalcDiv; + + if (!div) { + div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef + document.body.appendChild(div); // eslint-disable-line no-undef } - context = canvasContext; + var ds = div.style; - if (fill === 'filled' || fill === 'both') { - if (usePaths) { - context.fill(path); - } else { - context.fill(); - } + // from ele style + ds.fontFamily = family; + ds.fontStyle = fStyle; + ds.fontSize = size; + ds.fontWeight = weight; + + // forced style + ds.position = 'absolute'; + ds.left = '-9999px'; + ds.top = '-9999px'; + ds.zIndex = '-1'; + ds.visibility = 'hidden'; + ds.pointerEvents = 'none'; + ds.padding = '0'; + ds.lineHeight = '1'; + + if (ele.pstyle('text-wrap').value === 'wrap') { + ds.whiteSpace = 'pre'; // so newlines are taken into account + } else { + ds.whiteSpace = 'normal'; } - if (fill === 'hollow' || fill === 'both') { - context.lineWidth = shapeImpl.matchEdgeWidth ? edgeWidth : 1; - context.lineJoin = 'miter'; + // put label content in div + div.textContent = text; - if (usePaths) { - context.stroke(path); - } else { - context.stroke(); - } + cache[cacheKey] = { + width: Math.ceil(div.clientWidth / sizeMult), + height: Math.ceil(div.clientHeight / sizeMult) + }; + + return cache[cacheKey]; +}; + +BRp.calculateLabelAngles = function (ele) { + var _p = ele._private; + var rs = _p.rscratch; + var isEdge = ele.isEdge(); + var rot = ele.pstyle('text-rotation'); + var rotStr = rot.strValue; + + if (rotStr === 'none') { + rs.labelAngle = rs.sourceLabelAngle = rs.targetLabelAngle = 0; + } else if (isEdge && rotStr === 'autorotate') { + rs.labelAngle = Math.atan(rs.midDispY / rs.midDispX); + rs.sourceLabelAngle = rs.sourceLabelAutoAngle; + rs.targetLabelAngle = rs.targetLabelAutoAngle; + } else if (rotStr === 'autorotate') { + rs.labelAngle = rs.sourceLabelAngle = rs.targetLabelAngle = 0; + } else { + rs.labelAngle = rs.sourceLabelAngle = rs.targetLabelAngle = rot.pfValue; } }; -module.exports = CRp; +module.exports = BRp; /***/ }), -/* 103 */ +/* 116 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var math = __webpack_require__(2); - -var CRp = {}; +var BRp = {}; -CRp.drawElement = function (context, ele, shiftToOriginWithBb, showLabel) { +BRp.getNodeShape = function (node) { var r = this; + var shape = node.pstyle('shape').value; - if (ele.isNode()) { - r.drawNode(context, ele, shiftToOriginWithBb, showLabel); - } else { - r.drawEdge(context, ele, shiftToOriginWithBb, showLabel); + if (node.isParent()) { + if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') { + return shape; + } else { + return 'rectangle'; + } } -}; -CRp.drawCachedElement = function (context, ele, pxRatio, extent) { - var r = this; - var bb = ele.boundingBox(); + if (shape === 'polygon') { + var points = node.pstyle('shape-polygon-points').value; - if (bb.w === 0 || bb.h === 0) { - return; + return r.nodeShapes.makePolygon(points).name; } - if (!extent || math.boundingBoxesIntersect(bb, extent)) { - var cache = r.data.eleTxrCache.getElement(ele, bb, pxRatio); - - if (cache != null) { - context.drawImage(cache.texture.canvas, cache.x, 0, cache.width, cache.height, bb.x1, bb.y1, bb.w, bb.h); - } else { - // if the element is not cacheable, then draw directly - r.drawElement(context, ele); - } - } + return shape; }; -CRp.drawElements = function (context, eles) { - var r = this; - - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; +module.exports = BRp; - r.drawElement(context, ele); - } -}; +/***/ }), +/* 117 */ +/***/ (function(module, exports, __webpack_require__) { -CRp.drawCachedElements = function (context, eles, pxRatio, extent) { - var r = this; +"use strict"; - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - r.drawCachedElement(context, ele, pxRatio, extent); - } -}; +var BRp = {}; -CRp.drawCachedNodes = function (context, eles, pxRatio, extent) { +BRp.registerCalculationListeners = function () { + var cy = this.cy; + var elesToUpdate = cy.collection(); var r = this; - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - - if (!ele.isNode()) { - continue; - } + var enqueue = function enqueue(eles, e) { + var dirtyStyleCaches = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; - r.drawCachedElement(context, ele, pxRatio, extent); - } -}; + elesToUpdate.merge(eles); -CRp.drawLayeredElements = function (context, eles, pxRatio, extent) { - var r = this; + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var _p = ele._private; + var rstyle = _p.rstyle; - var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio); + if (dirtyStyleCaches) { + rstyle.clean = false; + _p.bbCache = null; + } - if (layers) { - for (var i = 0; i < layers.length; i++) { - var layer = layers[i]; - var bb = layer.bb; + var evts = rstyle.dirtyEvents = rstyle.dirtyEvents || { length: 0 }; - if (bb.w === 0 || bb.h === 0) { - continue; + if (!evts[e.type]) { + evts[e.type] = true; + evts.length++; } - - context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h); } - } else { - // fall back on plain caching if no layers - r.drawCachedElements(context, eles, pxRatio, extent); - } -}; - -CRp.drawDebugPoints = function (context, eles) { - var draw = function draw(x, y, color) { - context.fillStyle = color; - context.fillRect(x - 1, y - 1, 3, 3); }; - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var rs = ele._private.rscratch; - - if (ele.isNode()) { - var p = ele.position(); - - draw(p.x, p.y, 'magenta'); - } else { - var pts = rs.allpts; - - for (var j = 0; j + 1 < pts.length; j += 2) { - var x = pts[j]; - var y = pts[j + 1]; + r.binder(cy) + // nodes - draw(x, y, 'cyan'); - } + .on('position.* style.* free.* bounds.*', 'node', function onDirtyModNode(e) { + var node = e.target; - draw(rs.midX, rs.midY, 'yellow'); - } - } -}; + enqueue(node, e); + enqueue(node.connectedEdges(), e); + }).on('add.*', 'node', function onDirtyAddNode(e) { + var ele = e.target; -module.exports = CRp; + enqueue(ele, e); + }).on('background.*', 'node', function onDirtyBgNode(e) { + var ele = e.target; -/***/ }), -/* 104 */ -/***/ (function(module, exports, __webpack_require__) { + enqueue(ele, e, false); + }) -"use strict"; + // edges + .on('add.* style.*', 'edge', function onDirtyEdge(e) { + var edge = e.target; -var CRp = {}; + enqueue(edge, e); + enqueue(edge.parallelEdges(), e); + }).on('remove.*', 'edge', function onDirtyRemoveEdge(e) { + var edge = e.target; + var pEdges = edge.parallelEdges(); -CRp.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) { - var r = this; + for (var i = 0; i < pEdges.length; i++) { + var pEdge = pEdges[i]; - // detect problematic cases for old browsers with bad images (cheaper than try-catch) - if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) { - return; - } + if (!pEdge.removed()) { + enqueue(pEdge, e); + } + } + }) - context.drawImage(img, ix, iy, iw, ih, x, y, w, h); -}; + // manual dirtying -CRp.drawInscribedImage = function (context, img, node, index, nodeOpacity) { - var r = this; - var pos = node.position(); - var nodeX = pos.x; - var nodeY = pos.y; - var styleObj = node.cy().style(); - var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj); - var fit = getIndexedStyle(node, 'background-fit', 'value', index); - var repeat = getIndexedStyle(node, 'background-repeat', 'value', index); - var nodeW = node.width(); - var nodeH = node.height(); - var paddingX2 = node.padding() * 2; - var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2); - var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2); - var rs = node._private.rscratch; - var clip = node.pstyle('background-clip').value; - var shouldClip = clip === 'node'; - var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity; + .on('dirty.*', 'node', function onDirtyEle(e) { + var ele = e.target; - var imgW = img.width || img.cachedW; - var imgH = img.height || img.cachedH; + enqueue(ele, e); + }); - // workaround for broken browsers like ie - if (null == imgW || null == imgH) { - document.body.appendChild(img); // eslint-disable-line no-undef + var updateEleCalcs = function updateEleCalcs(willDraw) { + if (willDraw) { + var fns = r.onUpdateEleCalcsFns; - imgW = img.cachedW = img.width || img.offsetWidth; - imgH = img.cachedH = img.height || img.offsetHeight; + if (fns) { + for (var i = 0; i < fns.length; i++) { + var fn = fns[i]; - document.body.removeChild(img); // eslint-disable-line no-undef - } + fn(willDraw, elesToUpdate); + } + } - var w = imgW; - var h = imgH; + r.recalculateRenderedStyle(elesToUpdate, false); - if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') { - if (getIndexedStyle(node, 'background-width', 'units', index) === '%') { - w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW; - } else { - w = getIndexedStyle(node, 'background-width', 'pfValue', index); - } - } + for (var i = 0; i < elesToUpdate.length; i++) { + elesToUpdate[i]._private.rstyle.dirtyEvents = null; + } - if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') { - if (getIndexedStyle(node, 'background-height', 'units', index) === '%') { - h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH; - } else { - h = getIndexedStyle(node, 'background-height', 'pfValue', index); + elesToUpdate = cy.collection(); } - } + }; - if (w === 0 || h === 0) { - return; // no point in drawing empty image (and chrome is broken in this case) - } + r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs); +}; - if (fit === 'contain') { - var scale = Math.min(nodeTW / w, nodeTH / h); +BRp.onUpdateEleCalcs = function (fn) { + var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || []; - w *= scale; - h *= scale; - } else if (fit === 'cover') { - var scale = Math.max(nodeTW / w, nodeTH / h); + fns.push(fn); +}; - w *= scale; - h *= scale; - } +BRp.recalculateRenderedStyle = function (eles, useCache) { + var edges = []; + var nodes = []; - var x = nodeX - nodeTW / 2; // left - if (getIndexedStyle(node, 'background-position-x', 'units', index) === '%') { - x += (nodeTW - w) * getIndexedStyle(node, 'background-position-x', 'pfValue', index); - } else { - x += getIndexedStyle(node, 'background-position-x', 'pfValue', index); + // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox() + if (this.destroyed) { + return; } - var y = nodeY - nodeTH / 2; // top - if (getIndexedStyle(node, 'background-position-y', 'units', index) === '%') { - y += (nodeTH - h) * getIndexedStyle(node, 'background-position-y', 'pfValue', index); - } else { - y += getIndexedStyle(node, 'background-position-y', 'pfValue', index); + // use cache by default for perf + if (useCache === undefined) { + useCache = true; } - if (rs.pathCache) { - x -= nodeX; - y -= nodeY; + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var _p = ele._private; + var rstyle = _p.rstyle; - nodeX = 0; - nodeY = 0; - } + // only update if dirty and in graph + if (useCache && rstyle.clean || ele.removed()) { + continue; + } - var gAlpha = context.globalAlpha; + // only update if not display: none + if (ele.pstyle('display').value === 'none') { + continue; + } - context.globalAlpha = imgOpacity; + if (_p.group === 'nodes') { + nodes.push(ele); + } else { + // edges + edges.push(ele); + } - if (repeat === 'no-repeat') { + rstyle.clean = true; + // rstyle.dirtyEvents = null; + } - if (shouldClip) { - context.save(); + // update node data from projections + for (var i = 0; i < nodes.length; i++) { + var ele = nodes[i]; + var _p = ele._private; + var rstyle = _p.rstyle; + var pos = ele.position(); - if (rs.pathCache) { - context.clip(rs.pathCache); - } else { - r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH); + this.recalculateNodeLabelProjection(ele); - context.clip(); - } - } + rstyle.nodeX = pos.x; + rstyle.nodeY = pos.y; + rstyle.nodeW = ele.pstyle('width').pfValue; + rstyle.nodeH = ele.pstyle('height').pfValue; + } - r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h); + this.recalculateEdgeProjections(edges); - if (shouldClip) { - context.restore(); - } - } else { - var pattern = context.createPattern(img, repeat); - context.fillStyle = pattern; + // update edge data from projections + for (var i = 0; i < edges.length; i++) { + var ele = edges[i]; + var _p = ele._private; + var rstyle = _p.rstyle; + var rs = _p.rscratch; - r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH); + this.recalculateEdgeLabelProjections(ele); - context.translate(x, y); - context.fill(); - context.translate(-x, -y); + // update rstyle positions + rstyle.srcX = rs.arrowStartX; + rstyle.srcY = rs.arrowStartY; + rstyle.tgtX = rs.arrowEndX; + rstyle.tgtY = rs.arrowEndY; + rstyle.midX = rs.midX; + rstyle.midY = rs.midY; + rstyle.labelAngle = rs.labelAngle; + rstyle.sourceLabelAngle = rs.sourceLabelAngle; + rstyle.targetLabelAngle = rs.targetLabelAngle; } - - context.globalAlpha = gAlpha; }; -module.exports = CRp; +module.exports = BRp; /***/ }), -/* 105 */ +/* 118 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var util = __webpack_require__(1); -var math = __webpack_require__(2); +var zIndexSort = __webpack_require__(17); -var CRp = {}; +var BRp = {}; -CRp.eleTextBiggerThanMin = function (ele, scale) { - if (!scale) { - var zoom = ele.cy().zoom(); - var pxRatio = this.getPixelRatio(); - var lvl = Math.ceil(math.log2(zoom * pxRatio)); // the effective texture level +BRp.updateCachedGrabbedEles = function () { + var eles = this.cachedZSortedEles; - scale = Math.pow(2, lvl); + if (!eles) { + // just let this be recalculated on the next z sort tick + return; } - var computedSize = ele.pstyle('font-size').pfValue * scale; - var minSize = ele.pstyle('min-zoomed-font-size').pfValue; - - if (computedSize < minSize) { - return false; - } + eles.drag = []; + eles.nondrag = []; - return true; -}; + var grabTargets = []; -CRp.drawElementText = function (context, ele, force) { - var r = this; + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var rs = ele._private.rscratch; - if (force === undefined) { - if (!r.eleTextBiggerThanMin(ele)) { - return; - } - } else { - if (!force) { - return; + if (ele.grabbed() && !ele.isParent()) { + grabTargets.push(ele); + } else if (rs.inDragLayer) { + eles.drag.push(ele); + } else { + eles.nondrag.push(ele); } } - if (ele.isNode()) { - var label = ele.pstyle('label'); + // put the grab target nodes last so it's on top of its neighbourhood + for (var i = 0; i < grabTargets.length; i++) { + var ele = grabTargets[i]; - if (!label || !label.value) { - return; - } + eles.drag.push(ele); + } +}; - var textHalign = ele.pstyle('text-halign').strValue; - var textValign = ele.pstyle('text-valign').strValue; +BRp.invalidateCachedZSortedEles = function () { + this.cachedZSortedEles = null; +}; - switch (textHalign) { - case 'left': - context.textAlign = 'right'; - break; +BRp.getCachedZSortedEles = function (forceRecalc) { + if (forceRecalc || !this.cachedZSortedEles) { + //console.time('cachezorder') - case 'right': - context.textAlign = 'left'; - break; + var eles = this.cy.mutableElements().toArray(); - default: - // e.g. center - context.textAlign = 'center'; - } + eles.sort(zIndexSort); - context.textBaseline = 'bottom'; - } else { - var label = ele.pstyle('label'); - var srcLabel = ele.pstyle('source-label'); - var tgtLabel = ele.pstyle('target-label'); + eles.interactive = eles.filter(function (ele) { + return ele.interactive(); + }); - if ((!label || !label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) { - return; - } + this.cachedZSortedEles = eles; - context.textAlign = 'center'; - context.textBaseline = 'bottom'; + this.updateCachedGrabbedEles(); + } else { + eles = this.cachedZSortedEles; } - r.drawText(context, ele); + return eles; +}; - if (ele.isEdge()) { - r.drawText(context, ele, 'source'); +module.exports = BRp; - r.drawText(context, ele, 'target'); - } -}; +/***/ }), +/* 119 */ +/***/ (function(module, exports, __webpack_require__) { -CRp.drawNodeText = CRp.drawEdgeText = CRp.drawElementText; +"use strict"; -CRp.getFontCache = function (context) { - var cache; - this.fontCaches = this.fontCaches || []; +var BRp = {}; - for (var i = 0; i < this.fontCaches.length; i++) { - cache = this.fontCaches[i]; +BRp.getCachedImage = function (url, crossOrigin, onLoad) { + var r = this; + var imageCache = r.imageCache = r.imageCache || {}; + var cache = imageCache[url]; - if (cache.context === context) { - return cache; + if (cache) { + if (!cache.image.complete) { + cache.image.addEventListener('load', onLoad); } - } - cache = { - context: context - }; - this.fontCaches.push(cache); + return cache.image; + } else { + cache = imageCache[url] = imageCache[url] || {}; + + var image = cache.image = new Image(); // eslint-disable-line no-undef + + image.addEventListener('load', onLoad); + image.addEventListener('error', function () { + image.error = true; + }); + + // #1582 safari doesn't load data uris with crossOrigin properly + // https://bugs.webkit.org/show_bug.cgi?id=123978 + var dataUriPrefix = 'data:'; + var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix; + if (!isDataUri) { + image.crossOrigin = crossOrigin; // prevent tainted canvas + } + + image.src = url; - return cache; + return image; + } }; -// set up canvas context with font -// returns transformed text string -CRp.setupTextStyle = function (context, ele) { - // Font style - var parentOpacity = ele.effectiveOpacity(); - var labelStyle = ele.pstyle('font-style').strValue; - var labelSize = ele.pstyle('font-size').pfValue + 'px'; - var labelFamily = ele.pstyle('font-family').strValue; - var labelWeight = ele.pstyle('font-weight').strValue; - var opacity = ele.pstyle('text-opacity').value * ele.pstyle('opacity').value * parentOpacity; - var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity; - var color = ele.pstyle('color').value; - var outlineColor = ele.pstyle('text-outline-color').value; +module.exports = BRp; - var fontCacheKey = ele._private.fontKey; - var cache = this.getFontCache(context); +/***/ }), +/* 120 */ +/***/ (function(module, exports, __webpack_require__) { - if (cache.key !== fontCacheKey) { - context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily; +"use strict"; - cache.key = fontCacheKey; - } - // Calculate text draw position based on text alignment +var is = __webpack_require__(0); +var util = __webpack_require__(1); +var math = __webpack_require__(2); +var Event = __webpack_require__(16); - // so text outlines aren't jagged - context.lineJoin = 'round'; +var BRp = {}; - this.fillStyle(context, color[0], color[1], color[2], opacity); +BRp.registerBinding = function (target, event, handler, useCapture) { + var args = Array.prototype.slice.apply(arguments, [1]); // copy + var b = this.binder(target); - this.strokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity); + return b.on.apply(b, args); }; -function roundRect(ctx, x, y, width, height, radius) { - var radius = radius || 5; - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - ctx.fill(); -} +BRp.binder = function (tgt) { + var r = this; -// Draw text -CRp.drawText = function (context, ele, prefix) { - var _p = ele._private; - var rscratch = _p.rscratch; - var parentOpacity = ele.effectiveOpacity(); - if (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0) { - return; - } + var tgtIsDom = tgt === window || tgt === document || tgt === document.body || is.domElement(tgt); - var textX = util.getPrefixedProperty(rscratch, 'labelX', prefix); - var textY = util.getPrefixedProperty(rscratch, 'labelY', prefix); - var text = this.getLabelText(ele, prefix); + if (r.supportsPassiveEvents == null) { - if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) { - this.setupTextStyle(context, ele); + // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection + var supportsPassive = false; + try { + var opts = Object.defineProperty({}, 'passive', { + get: function get() { + supportsPassive = true; + } + }); - var pdash = prefix ? prefix + '-' : ''; - var textW = util.getPrefixedProperty(rscratch, 'labelWidth', prefix); - var textH = util.getPrefixedProperty(rscratch, 'labelHeight', prefix); - var textAngle = util.getPrefixedProperty(rscratch, 'labelAngle', prefix); - var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue; - var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue; + window.addEventListener('test', null, opts); + } catch (err) {} - var isEdge = ele.isEdge(); - var isNode = ele.isNode(); + r.supportsPassiveEvents = supportsPassive; + } - var halign = ele.pstyle('text-halign').value; - var valign = ele.pstyle('text-valign').value; + var on = function on(event, handler, useCapture) { + var args = Array.prototype.slice.call(arguments); - if (isEdge) { - halign = 'center'; - valign = 'center'; + if (tgtIsDom && r.supportsPassiveEvents) { + // replace useCapture w/ opts obj + args[2] = { + capture: useCapture != null ? useCapture : false, + passive: false, + once: false + }; } - textX += marginX; - textY += marginY; + r.bindings.push({ + target: tgt, + args: args + }); - var rotation = ele.pstyle('text-rotation'); - var theta; + (tgt.addEventListener || tgt.on).apply(tgt, args); - if (rotation.strValue === 'autorotate') { - theta = isEdge ? textAngle : 0; - } else if (rotation.strValue === 'none') { - theta = 0; - } else { - theta = rotation.pfValue; - } + return this; + }; - if (theta !== 0) { - var orgTextX = textX; - var orgTextY = textY; + return { + on: on, + addEventListener: on, + addListener: on, + bind: on + }; +}; - context.translate(orgTextX, orgTextY); - context.rotate(theta); +BRp.nodeIsDraggable = function (node) { + return node && node.isNode() && !node.locked() && node.grabbable(); +}; - textX = 0; - textY = 0; - } +BRp.nodeIsGrabbable = function (node) { + return this.nodeIsDraggable(node) && node.interactive(); +}; - switch (valign) { - case 'top': - break; - case 'center': - textY += textH / 2; - break; - case 'bottom': - textY += textH; - break; +BRp.load = function () { + var r = this; + + var triggerEvents = function triggerEvents(target, names, e, props) { + if (target == null) { + target = r.cy; } - var backgroundOpacity = ele.pstyle('text-background-opacity').value; - var borderOpacity = ele.pstyle('text-border-opacity').value; - var textBorderWidth = ele.pstyle('text-border-width').pfValue; - var backgroundPadding = ele.pstyle('text-background-padding').pfValue; + for (var i = 0; i < names.length; i++) { + var name = names[i]; - if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) { - var bgX = textX - backgroundPadding; + target.emit(util.extend({ originalEvent: e, type: name }, props)); + } + }; - switch (halign) { - case 'left': - bgX -= textW; - break; - case 'center': - bgX -= textW / 2; - break; - case 'right': - break; - } + var isMultSelKeyDown = function isMultSelKeyDown(e) { + return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey + }; - var bgY = textY - textH - backgroundPadding; - var bgW = textW + 2 * backgroundPadding; - var bgH = textH + 2 * backgroundPadding; + var allowPanningPassthrough = function allowPanningPassthrough(down, downs) { + var allowPassthrough = true; - if (backgroundOpacity > 0) { - var textFill = context.fillStyle; - var textBackgroundColor = ele.pstyle('text-background-color').value; + if (r.cy.hasCompoundNodes() && down && down.isEdge()) { + // a compound node below the edge => no passthrough panning + for (var i = 0; downs && i < downs.length; i++) { + var down = downs[i]; - context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')'; - var styleShape = ele.pstyle('text-background-shape').strValue; - if (styleShape == 'roundrectangle') { - roundRect(context, bgX, bgY, bgW, bgH, 2); - } else { - context.fillRect(bgX, bgY, bgW, bgH); + if (down.isNode() && down.isParent()) { + allowPassthrough = false; + break; } - context.fillStyle = textFill; } + } else { + allowPassthrough = true; + } - if (textBorderWidth > 0 && borderOpacity > 0) { - var textStroke = context.strokeStyle; - var textLineWidth = context.lineWidth; - var textBorderColor = ele.pstyle('text-border-color').value; - var textBorderStyle = ele.pstyle('text-border-style').value; - - context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')'; - context.lineWidth = textBorderWidth; - - if (context.setLineDash) { - // for very outofdate browsers - switch (textBorderStyle) { - case 'dotted': - context.setLineDash([1, 1]); - break; - case 'dashed': - context.setLineDash([4, 2]); - break; - case 'double': - context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders - context.setLineDash([]); - break; - case 'solid': - context.setLineDash([]); - break; - } - } + return allowPassthrough; + }; - context.strokeRect(bgX, bgY, bgW, bgH); + var getDragListIds = function getDragListIds(opts) { + var listHasId; - if (textBorderStyle === 'double') { - var whiteWidth = textBorderWidth / 2; + if (opts.addToList && r.cy.hasCompoundNodes()) { + // only needed for compound graphs + if (!opts.addToList.hasId) { + // build ids lookup if doesn't already exist + opts.addToList.hasId = {}; - context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2); - } + for (var i = 0; i < opts.addToList.length; i++) { + var ele = opts.addToList[i]; - if (context.setLineDash) { - // for very outofdate browsers - context.setLineDash([]); + opts.addToList.hasId[ele.id()] = true; } - context.lineWidth = textLineWidth; - context.strokeStyle = textStroke; } + + listHasId = opts.addToList.hasId; } - var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle + return listHasId || {}; + }; - if (lineWidth > 0) { - context.lineWidth = lineWidth; - } + var setGrabbed = function setGrabbed(ele) { + ele[0]._private.grabbed = true; + }; - if (ele.pstyle('text-wrap').value === 'wrap') { - var lines = util.getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix); - var lineHeight = textH / lines.length; + var setFreed = function setFreed(ele) { + ele[0]._private.grabbed = false; + }; - switch (valign) { - case 'top': - textY -= (lines.length - 1) * lineHeight; - break; - case 'center': - case 'bottom': - textY -= (lines.length - 1) * lineHeight; - break; - } + var setInDragLayer = function setInDragLayer(ele) { + ele[0]._private.rscratch.inDragLayer = true; + }; - for (var l = 0; l < lines.length; l++) { - if (lineWidth > 0) { - context.strokeText(lines[l], textX, textY); - } + var setOutDragLayer = function setOutDragLayer(ele) { + ele[0]._private.rscratch.inDragLayer = false; + }; - context.fillText(lines[l], textX, textY); + var setGrabTarget = function setGrabTarget(ele) { + ele[0]._private.rscratch.isGrabTarget = true; + }; - textY += lineHeight; - } - } else { - if (lineWidth > 0) { - context.strokeText(text, textX, textY); - } + var removeGrabTarget = function removeGrabTarget(ele) { + ele[0]._private.rscratch.isGrabTarget = false; + }; - context.fillText(text, textX, textY); - } + var addToDragList = function addToDragList(ele, opts) { + var listHasId = getDragListIds(opts); - if (theta !== 0) { - context.rotate(-theta); - context.translate(-orgTextX, -orgTextY); - } - } -}; + if (!listHasId[ele.id()]) { + opts.addToList.push(ele); + listHasId[ele.id()] = true; -module.exports = CRp; + setGrabbed(ele); + } + }; -/***/ }), -/* 106 */ -/***/ (function(module, exports, __webpack_require__) { + // helper function to determine which child nodes and inner edges + // of a compound node to be dragged as well as the grabbed and selected nodes + var addDescendantsToDrag = function addDescendantsToDrag(node, opts) { + if (!node.cy().hasCompoundNodes()) { + return; + } -"use strict"; + if (opts.inDragLayer == null && opts.addToList == null) { + return; + } // nothing to do + var innerNodes = node.descendants(); -/* global Path2D */ + if (opts.inDragLayer) { + innerNodes.forEach(setInDragLayer); + innerNodes.connectedEdges().forEach(setInDragLayer); + } -var is = __webpack_require__(0); + if (opts.addToList) { + innerNodes.forEach(function (ele) { + addToDragList(ele, opts); + }); + } + }; -var CRp = {}; + // adds the given nodes and its neighbourhood to the drag layer + var addNodesToDrag = function addNodesToDrag(nodes, opts) { + opts = opts || {}; -CRp.drawNode = function (context, node, shiftToOriginWithBb, drawLabel) { - var r = this; - var nodeWidth = void 0, - nodeHeight = void 0; - var _p = node._private; - var rs = _p.rscratch; - var pos = node.position(); + var hasCompoundNodes = nodes.cy().hasCompoundNodes(); - if (!is.number(pos.x) || !is.number(pos.y)) { - return; // can't draw node with undefined position - } + if (opts.inDragLayer) { + nodes.forEach(setInDragLayer); - if (!node.visible()) { - return; - } + nodes.neighborhood().stdFilter(function (ele) { + return !hasCompoundNodes || ele.isEdge(); + }).forEach(setInDragLayer); + } - var parentOpacity = node.effectiveOpacity(); + if (opts.addToList) { + nodes.forEach(function (ele) { + addToDragList(ele, opts); + }); + } - var usePaths = r.usePaths(); - var path = void 0; - var pathCacheHit = false; + addDescendantsToDrag(nodes, opts); // always add to drag - var padding = node.padding(); + // also add nodes and edges related to the topmost ancestor + updateAncestorsInDragLayer(nodes, { + inDragLayer: opts.inDragLayer + }); - nodeWidth = node.width() + 2 * padding; - nodeHeight = node.height() + 2 * padding; + r.updateCachedGrabbedEles(); + }; - // - // setup shift + var addNodeToDrag = addNodesToDrag; - var bb = void 0; - if (shiftToOriginWithBb) { - bb = shiftToOriginWithBb; + var freeDraggedElements = function freeDraggedElements(grabbedEles) { + if (!grabbedEles) { + return; + } - context.translate(-bb.x1, -bb.y1); - } + grabbedEles.hasId = {}; // clear the id list - // - // load bg image + // just go over all elements rather than doing a bunch of (possibly expensive) traversals + r.getCachedZSortedEles().forEach(function (ele) { + setFreed(ele); + setOutDragLayer(ele); + removeGrabTarget(ele); + }); - var bgImgProp = node.pstyle('background-image'); - var urls = bgImgProp.value; - var urlDefined = new Array(urls.length); - var image = new Array(urls.length); - var numImages = 0; - for (var i = 0; i < urls.length; i++) { - var url = urls[i]; - var defd = urlDefined[i] = url != null && url !== 'none'; + r.updateCachedGrabbedEles(); + }; - if (defd) { - var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i); + // helper function to determine which ancestor nodes and edges should go + // to the drag layer (or should be removed from drag layer). + var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) { - numImages++; + if (opts.inDragLayer == null && opts.addToList == null) { + return; + } // nothing to do - // get image, and if not loaded then ask to redraw when later loaded - image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () { - node.emitAndNotify('background'); - }); + if (!node.cy().hasCompoundNodes()) { + return; } - } - // - // setup styles - - var darkness = node.pstyle('background-blacken').value; - var borderWidth = node.pstyle('border-width').pfValue; - var bgColor = node.pstyle('background-color').value; - var bgOpacity = node.pstyle('background-opacity').value * parentOpacity; - var borderColor = node.pstyle('border-color').value; - var borderStyle = node.pstyle('border-style').value; - var borderOpacity = node.pstyle('border-opacity').value * parentOpacity; + // find top-level parent + var parent = node.ancestors().orphans(); - context.lineJoin = 'miter'; // so borders are square with the node shape + // no parent node: no nodes to add to the drag layer + if (parent.same(node)) { + return; + } - var setupShapeColor = function setupShapeColor() { - var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity; + var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants()); - r.fillStyle(context, bgColor[0], bgColor[1], bgColor[2], bgOpy); - }; + var edges = nodes.connectedEdges(); - var setupBorderColor = function setupBorderColor() { - var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity; + if (opts.inDragLayer) { + edges.forEach(setInDragLayer); + nodes.forEach(setInDragLayer); + } - r.strokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy); + if (opts.addToList) { + nodes.forEach(function (ele) { + addToDragList(ele, opts); + }); + } }; - // - // setup shape + var haveMutationsApi = typeof MutationObserver !== 'undefined'; - var styleShape = node.pstyle('shape').strValue; - var shapePts = node.pstyle('shape-polygon-points').pfValue; + // watch for when the cy container is removed from the dom + if (haveMutationsApi) { + r.removeObserver = new MutationObserver(function (mutns) { + // eslint-disable-line no-undef + for (var i = 0; i < mutns.length; i++) { + var mutn = mutns[i]; + var rNodes = mutn.removedNodes; - if (usePaths) { - var pathCacheKey = styleShape + '$' + nodeWidth + '$' + nodeHeight + (styleShape === 'polygon' ? '$' + shapePts.join('$') : ''); + if (rNodes) { + for (var j = 0; j < rNodes.length; j++) { + var rNode = rNodes[j]; - context.translate(pos.x, pos.y); + if (rNode === r.container) { + r.destroy(); + break; + } + } + } + } + }); - if (rs.pathCacheKey === pathCacheKey) { - path = rs.pathCache; - pathCacheHit = true; - } else { - path = new Path2D(); - rs.pathCacheKey = pathCacheKey; - rs.pathCache = path; + if (r.container.parentNode) { + r.removeObserver.observe(r.container.parentNode, { childList: true }); } + } else { + r.registerBinding(r.container, 'DOMNodeRemoved', function (e) { + r.destroy(); + }); } - var drawShape = function drawShape() { - if (!pathCacheHit) { + var onResize = util.debounce(function () { + r.cy.resize(); + }, 100); - var npos = pos; + if (haveMutationsApi) { + r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef - if (usePaths) { - npos = { - x: 0, - y: 0 - }; - } + r.styleObserver.observe(r.container, { attributes: true }); + } - r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight); - } + // auto resize + r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef - if (usePaths) { - context.fill(path); - } else { - context.fill(); + var forEachUp = function forEachUp(domEle, fn) { + while (domEle != null) { + fn(domEle); + + domEle = domEle.parentNode; } }; - var drawImages = function drawImages() { - var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : parentOpacity; + var invalidateCoords = function invalidateCoords() { + r.invalidateContainerClientCoordsCache(); + }; - var prevBging = _p.backgrounding; - var totalCompleted = 0; + forEachUp(r.container, function (domEle) { + r.registerBinding(domEle, 'transitionend', invalidateCoords); + r.registerBinding(domEle, 'animationend', invalidateCoords); + r.registerBinding(domEle, 'scroll', invalidateCoords); + }); - for (var _i = 0; _i < image.length; _i++) { - if (urlDefined[_i] && image[_i].complete && !image[_i].error) { - totalCompleted++; - r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity); - } - } + // stop right click menu from appearing on cy + r.registerBinding(r.container, 'contextmenu', function (e) { + e.preventDefault(); + }); - _p.backgrounding = !(totalCompleted === numImages); - if (prevBging !== _p.backgrounding) { - // update style b/c :backgrounding state changed - node.updateStyle(false); - } + var inBoxSelection = function inBoxSelection() { + return r.selection[4] !== 0; }; - var drawPie = function drawPie() { - var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; - var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : parentOpacity; + var eventInContainer = function eventInContainer(e) { + // save cycles if mouse events aren't to be captured + var containerPageCoords = r.findContainerClientCoords(); + var x = containerPageCoords[0]; + var y = containerPageCoords[1]; + var width = containerPageCoords[2]; + var height = containerPageCoords[3]; - if (r.hasPie(node)) { - r.drawPie(context, node, pieOpacity); + var positions = e.touches ? e.touches : [e]; + var atLeastOnePosInside = false; - // redraw/restore path if steps after pie need it - if (redrawShape) { + for (var i = 0; i < positions.length; i++) { + var p = positions[i]; - if (!usePaths) { - r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight); - } + if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) { + atLeastOnePosInside = true; + break; } } - }; - - var darken = function darken() { - var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : parentOpacity; - var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity; - var c = darkness > 0 ? 0 : 255; + if (!atLeastOnePosInside) { + return false; + } - if (darkness !== 0) { - r.fillStyle(context, c, c, c, opacity); + var container = r.container; + var target = e.target; + var tParent = target.parentNode; + var containerIsTarget = false; - if (usePaths) { - context.fill(path); - } else { - context.fill(); + while (tParent) { + if (tParent === container) { + containerIsTarget = true; + break; } + + tParent = tParent.parentNode; } - }; - var drawBorder = function drawBorder() { - if (borderWidth > 0) { + if (!containerIsTarget) { + return false; + } // if target is outisde cy container, then this event is not for us - context.lineWidth = borderWidth; - context.lineCap = 'butt'; + return true; + }; - if (context.setLineDash) { - // for very outofdate browsers - switch (borderStyle) { - case 'dotted': - context.setLineDash([1, 1]); - break; + // Primary key + r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) { + if (!eventInContainer(e)) { + return; + } - case 'dashed': - context.setLineDash([4, 2]); - break; + e.preventDefault(); + r.hoverData.capture = true; + r.hoverData.which = e.which; - case 'solid': - case 'double': - context.setLineDash([]); - break; - } - } + var cy = r.cy; + var gpos = [e.clientX, e.clientY]; + var pos = r.projectIntoViewport(gpos[0], gpos[1]); + var select = r.selection; + var nears = r.findNearestElements(pos[0], pos[1], true, false); + var near = nears[0]; + var draggedElements = r.dragData.possibleDragElements; - if (usePaths) { - context.stroke(path); - } else { - context.stroke(); - } + r.hoverData.mdownPos = pos; + r.hoverData.mdownGPos = gpos; - if (borderStyle === 'double') { - context.lineWidth = borderWidth / 3; + var checkForTaphold = function checkForTaphold() { + r.hoverData.tapholdCancelled = false; - var gco = context.globalCompositeOperation; - context.globalCompositeOperation = 'destination-out'; + clearTimeout(r.hoverData.tapholdTimeout); - if (usePaths) { - context.stroke(path); + r.hoverData.tapholdTimeout = setTimeout(function () { + + if (r.hoverData.tapholdCancelled) { + return; } else { - context.stroke(); + var ele = r.hoverData.down; + + if (ele) { + ele.emit({ + originalEvent: e, + type: 'taphold', + position: { x: pos[0], y: pos[1] } + }); + } else { + cy.emit({ + originalEvent: e, + type: 'taphold', + position: { x: pos[0], y: pos[1] } + }); + } } + }, r.tapholdDuration); + }; - context.globalCompositeOperation = gco; + // Right click button + if (e.which == 3) { + + r.hoverData.cxtStarted = true; + + var cxtEvt = { + originalEvent: e, + type: 'cxttapstart', + position: { x: pos[0], y: pos[1] } + }; + + if (near) { + near.activate(); + near.emit(cxtEvt); + + r.hoverData.down = near; + } else { + cy.emit(cxtEvt); } - // reset in case we changed the border style - if (context.setLineDash) { - // for very outofdate browsers - context.setLineDash([]); + r.hoverData.downTime = new Date().getTime(); + r.hoverData.cxtDragged = false; + + // Primary button + } else if (e.which == 1) { + + if (near) { + near.activate(); } - } - }; - var drawOverlay = function drawOverlay() { - var overlayPadding = node.pstyle('overlay-padding').pfValue; - var overlayOpacity = node.pstyle('overlay-opacity').value; - var overlayColor = node.pstyle('overlay-color').value; + // Element dragging + { + // If something is under the cursor and it is draggable, prepare to grab it + if (near != null) { - if (overlayOpacity > 0) { - r.fillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity); + if (r.nodeIsGrabbable(near)) { - r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2); + var makeEvent = function makeEvent(type) { + return { + originalEvent: e, + type: type, + position: { x: pos[0], y: pos[1] } + }; + }; - context.fill(); - } - }; + var triggerGrab = function triggerGrab(ele) { + ele.emit(makeEvent('grab')); + }; - var drawText = function drawText() { - r.drawElementText(context, node, drawLabel); - }; + setGrabTarget(near); - var ghost = node.pstyle('ghost').value === 'yes'; + if (!near.selected()) { - if (ghost) { - var gx = node.pstyle('ghost-offset-x').pfValue; - var gy = node.pstyle('ghost-offset-y').pfValue; - var ghostOpacity = node.pstyle('ghost-opacity').value; - var effGhostOpacity = ghostOpacity * parentOpacity; + draggedElements = r.dragData.possibleDragElements = []; + addNodeToDrag(near, { addToList: draggedElements }); - context.translate(gx, gy); + near.emit(makeEvent('grabon')).emit(makeEvent('grab')); + } else { + draggedElements = r.dragData.possibleDragElements = []; - setupShapeColor(ghostOpacity * bgOpacity); - drawShape(); - drawImages(effGhostOpacity); - drawPie(darkness !== 0 || borderWidth !== 0); - darken(effGhostOpacity); - setupBorderColor(ghostOpacity * borderOpacity); - drawBorder(); + var selectedNodes = cy.$(function (ele) { + return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele); + }); - context.translate(-gx, -gy); - } + addNodesToDrag(selectedNodes, { addToList: draggedElements }); - setupShapeColor(); - drawShape(); - drawImages(); - drawPie(darkness !== 0 || borderWidth !== 0); - darken(); - setupBorderColor(); - drawBorder(); + near.emit(makeEvent('grabon')); - if (usePaths) { - context.translate(-pos.x, -pos.y); - } + selectedNodes.forEach(triggerGrab); + } - drawText(); - drawOverlay(); + r.redrawHint('eles', true); + r.redrawHint('drag', true); + } + } - // - // clean up shift + r.hoverData.down = near; + r.hoverData.downs = nears; + r.hoverData.downTime = new Date().getTime(); + } - if (shiftToOriginWithBb) { - context.translate(bb.x1, bb.y1); - } -}; + triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, { + position: { x: pos[0], y: pos[1] } + }); -// does the node have at least one pie piece? -CRp.hasPie = function (node) { - node = node[0]; // ensure ele ref + if (near == null) { + select[4] = 1; - return node._private.hasPie; -}; + r.data.bgActivePosistion = { + x: pos[0], + y: pos[1] + }; -CRp.drawPie = function (context, node, nodeOpacity, pos) { - node = node[0]; // ensure ele ref - pos = pos || node.position(); + r.redrawHint('select', true); - var cyStyle = node.cy().style(); - var pieSize = node.pstyle('pie-size'); - var x = pos.x; - var y = pos.y; - var nodeW = node.width(); - var nodeH = node.height(); - var radius = Math.min(nodeW, nodeH) / 2; // must fit in node - var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1] - var usePaths = this.usePaths(); + r.redraw(); + } else if (near.isEdge()) { + select[4] = 1; // for future pan + } - if (usePaths) { - x = 0; - y = 0; - } + checkForTaphold(); + } - if (pieSize.units === '%') { - radius = radius * pieSize.pfValue; - } else if (pieSize.pfValue !== undefined) { - radius = pieSize.pfValue / 2; - } + // Initialize selection box coordinates + select[0] = select[2] = pos[0]; + select[1] = select[3] = pos[1]; + }, false); - for (var i = 1; i <= cyStyle.pieBackgroundN; i++) { - // 1..N - var size = node.pstyle('pie-' + i + '-background-size').value; - var color = node.pstyle('pie-' + i + '-background-color').value; - var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity; - var percent = size / 100; // map integer range [0, 100] to [0, 1] + r.registerBinding(window, 'mousemove', function mousemoveHandler(e) { + // eslint-disable-line no-undef + var capture = r.hoverData.capture; - // percent can't push beyond 1 - if (percent + lastPercent > 1) { - percent = 1 - lastPercent; + if (!capture && !eventInContainer(e)) { + return; } - var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise - var angleDelta = 2 * Math.PI * percent; - var angleEnd = angleStart + angleDelta; + var preventDefault = false; + var cy = r.cy; + var zoom = cy.zoom(); + var gpos = [e.clientX, e.clientY]; + var pos = r.projectIntoViewport(gpos[0], gpos[1]); + var mdownPos = r.hoverData.mdownPos; + var mdownGPos = r.hoverData.mdownGPos; + var select = r.selection; - // ignore if - // - zero size - // - we're already beyond the full circle - // - adding the current slice would go beyond the full circle - if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) { - continue; + var near = null; + if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) { + near = r.findNearestElement(pos[0], pos[1], true, false); } + var last = r.hoverData.last; + var down = r.hoverData.down; - context.beginPath(); - context.moveTo(x, y); - context.arc(x, y, radius, angleStart, angleEnd); - context.closePath(); - - this.fillStyle(context, color[0], color[1], color[2], opacity); + var disp = [pos[0] - select[2], pos[1] - select[3]]; - context.fill(); + var draggedElements = r.dragData.possibleDragElements; - lastPercent += percent; - } -}; + var isOverThresholdDrag; -module.exports = CRp; + if (mdownGPos) { + var dx = gpos[0] - mdownGPos[0]; + var dx2 = dx * dx; + var dy = gpos[1] - mdownGPos[1]; + var dy2 = dy * dy; + var dist2 = dx2 + dy2; -/***/ }), -/* 107 */ -/***/ (function(module, exports, __webpack_require__) { + r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2; + } -"use strict"; + var multSelKeyDown = isMultSelKeyDown(e); + if (isOverThresholdDrag) { + r.hoverData.tapholdCancelled = true; + } -var CRp = {}; + var updateDragDelta = function updateDragDelta() { + var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || []; -var util = __webpack_require__(1); + if (dragDelta.length === 0) { + dragDelta.push(disp[0]); + dragDelta.push(disp[1]); + } else { + dragDelta[0] += disp[0]; + dragDelta[1] += disp[1]; + } + }; -var motionBlurDelay = 100; + preventDefault = true; -// var isFirefox = typeof InstallTrigger !== 'undefined'; + triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, { + position: { x: pos[0], y: pos[1] } + }); -CRp.getPixelRatio = function () { - var context = this.data.contexts[0]; + var goIntoBoxMode = function goIntoBoxMode() { + r.data.bgActivePosistion = undefined; - if (this.forcedPixelRatio != null) { - return this.forcedPixelRatio; - } + if (!r.hoverData.selecting) { + cy.emit('boxstart'); + } - var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; + select[4] = 1; + r.hoverData.selecting = true; - return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef -}; + r.redrawHint('select', true); + r.redraw(); + }; -CRp.paintCache = function (context) { - var caches = this.paintCaches = this.paintCaches || []; - var needToCreateCache = true; - var cache; + // trigger context drag if rmouse down + if (r.hoverData.which === 3) { + // but only if over threshold + if (isOverThresholdDrag) { + var cxtEvt = { + originalEvent: e, + type: 'cxtdrag', + position: { x: pos[0], y: pos[1] } + }; - for (var i = 0; i < caches.length; i++) { - cache = caches[i]; + if (down) { + down.emit(cxtEvt); + } else { + cy.emit(cxtEvt); + } - if (cache.context === context) { - needToCreateCache = false; - break; - } - } + r.hoverData.cxtDragged = true; - if (needToCreateCache) { - cache = { - context: context - }; - caches.push(cache); - } + if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) { - return cache; -}; + if (r.hoverData.cxtOver) { + r.hoverData.cxtOver.emit({ + originalEvent: e, + type: 'cxtdragout', + position: { x: pos[0], y: pos[1] } + }); + } -CRp.fillStyle = function (context, r, g, b, a) { - context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + r.hoverData.cxtOver = near; - // turn off for now, seems context does its own caching + if (near) { + near.emit({ + originalEvent: e, + type: 'cxtdragover', + position: { x: pos[0], y: pos[1] } + }); + } + } + } - // var cache = this.paintCache(context); + // Check if we are drag panning the entire graph + } else if (r.hoverData.dragging) { + preventDefault = true; - // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + if (cy.panningEnabled() && cy.userPanningEnabled()) { + var deltaP; - // if( cache.fillStyle !== fillStyle ){ - // context.fillStyle = cache.fillStyle = fillStyle; - // } -}; + if (r.hoverData.justStartedPan) { + var mdPos = r.hoverData.mdownPos; -CRp.strokeStyle = function (context, r, g, b, a) { - context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + deltaP = { + x: (pos[0] - mdPos[0]) * zoom, + y: (pos[1] - mdPos[1]) * zoom + }; - // turn off for now, seems context does its own caching + r.hoverData.justStartedPan = false; + } else { + deltaP = { + x: disp[0] * zoom, + y: disp[1] * zoom + }; + } - // var cache = this.paintCache(context); + cy.panBy(deltaP); - // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + r.hoverData.dragged = true; + } - // if( cache.strokeStyle !== strokeStyle ){ - // context.strokeStyle = cache.strokeStyle = strokeStyle; - // } -}; + // Needs reproject due to pan changing viewport + pos = r.projectIntoViewport(e.clientX, e.clientY); -// Resize canvas -CRp.matchCanvasSize = function (container) { - var r = this; - var data = r.data; - var bb = r.findContainerClientCoords(); - var width = bb[2]; - var height = bb[3]; - var pixelRatio = r.getPixelRatio(); - var mbPxRatio = r.motionBlurPxRatio; + // Checks primary button down & out of time & mouse not moved much + } else if (select[4] == 1 && (down == null || down.isEdge())) { - if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) { - pixelRatio = mbPxRatio; - } + if (isOverThresholdDrag) { - var canvasWidth = width * pixelRatio; - var canvasHeight = height * pixelRatio; - var canvas; + if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) { + goIntoBoxMode(); + } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) { + var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs); - if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) { - return; // save cycles if same - } + if (allowPassthrough) { + r.hoverData.dragging = true; + r.hoverData.justStartedPan = true; + select[4] = 0; - r.fontCaches = null; // resizing resets the style + r.data.bgActivePosistion = math.array2point(mdownPos); - var canvasContainer = data.canvasContainer; - canvasContainer.style.width = width + 'px'; - canvasContainer.style.height = height + 'px'; + r.redrawHint('select', true); + r.redraw(); + } + } - for (var i = 0; i < r.CANVAS_LAYERS; i++) { - canvas = data.canvases[i]; + if (down && down.isEdge() && down.active()) { + down.unactivate(); + } + } + } else { + if (down && down.isEdge() && down.active()) { + down.unactivate(); + } - canvas.width = canvasWidth; - canvas.height = canvasHeight; + if ((!down || !down.grabbed()) && near != last) { - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - } + if (last) { + triggerEvents(last, ['mouseout', 'tapdragout'], e, { + position: { x: pos[0], y: pos[1] } + }); + } - for (var i = 0; i < r.BUFFER_COUNT; i++) { - canvas = data.bufferCanvases[i]; + if (near) { + triggerEvents(near, ['mouseover', 'tapdragover'], e, { + position: { x: pos[0], y: pos[1] } + }); + } - canvas.width = canvasWidth; - canvas.height = canvasHeight; + r.hoverData.last = near; + } - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - } + if (down) { - r.textureMult = 1; - if (pixelRatio <= 1) { - canvas = data.bufferCanvases[r.TEXTURE_BUFFER]; + if (isOverThresholdDrag) { + // then we can take action - r.textureMult = 2; - canvas.width = canvasWidth * r.textureMult; - canvas.height = canvasHeight * r.textureMult; - } + if (cy.boxSelectionEnabled() && multSelKeyDown) { + // then selection overrides + if (down && down.grabbed()) { + freeDraggedElements(draggedElements); - r.canvasWidth = canvasWidth; - r.canvasHeight = canvasHeight; -}; + down.emit('free'); + } -CRp.renderTo = function (cxt, zoom, pan, pxRatio) { - this.render({ - forcedContext: cxt, - forcedZoom: zoom, - forcedPan: pan, - drawAllLayers: true, - forcedPxRatio: pxRatio - }); -}; + goIntoBoxMode(); + } else if (down && down.grabbed() && r.nodeIsDraggable(down)) { + // drag node + var justStartedDrag = !r.dragData.didDrag; -CRp.render = function (options) { - options = options || util.staticEmptyObject(); + if (justStartedDrag) { + r.redrawHint('eles', true); + } - var forcedContext = options.forcedContext; - var drawAllLayers = options.drawAllLayers; - var drawOnlyNodeLayer = options.drawOnlyNodeLayer; - var forcedZoom = options.forcedZoom; - var forcedPan = options.forcedPan; - var r = this; - var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio; - var cy = r.cy;var data = r.data; - var needDraw = data.canvasNeedsRedraw; - var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming); - var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur; - var mbPxRatio = r.motionBlurPxRatio; - var hasCompoundNodes = cy.hasCompoundNodes(); - var inNodeDragGesture = r.hoverData.draggingEles; - var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false; - motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection; - var motionBlurFadeEffect = motionBlur; + r.dragData.didDrag = true; // indicate that we actually did drag the node - if (!forcedContext) { - if (r.prevPxRatio !== pixelRatio) { - r.invalidateContainerClientCoordsCache(); - r.matchCanvasSize(r.container); + var toTrigger = []; - r.redrawHint('eles', true); - r.redrawHint('drag', true); - } + // now, add the elements to the drag layer if not done already + if (!r.hoverData.draggingEles) { + addNodesToDrag(cy.collection(draggedElements), { inDragLayer: true }); + } - r.prevPxRatio = pixelRatio; - } + for (var i = 0; i < draggedElements.length; i++) { + var dEle = draggedElements[i]; - if (!forcedContext && r.motionBlurTimeout) { - clearTimeout(r.motionBlurTimeout); - } + // Locked nodes not draggable, as well as non-visible nodes + if (r.nodeIsDraggable(dEle) && dEle.grabbed()) { + var dPos = dEle.position(); - if (motionBlur) { - if (r.mbFrames == null) { - r.mbFrames = 0; - } + toTrigger.push(dEle); - r.mbFrames++; + if (is.number(disp[0]) && is.number(disp[1])) { + dPos.x += disp[0]; + dPos.y += disp[1]; - if (r.mbFrames < 3) { - // need several frames before even high quality motionblur - motionBlurFadeEffect = false; - } + if (justStartedDrag) { + var dragDelta = r.hoverData.dragDelta; - // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing) - if (r.mbFrames > r.minMbLowQualFrames) { - //r.fullQualityMb = false; - r.motionBlurPxRatio = r.mbPxRBlurry; - } - } + if (dragDelta && is.number(dragDelta[0]) && is.number(dragDelta[1])) { + dPos.x += dragDelta[0]; + dPos.y += dragDelta[1]; + } + } + } + } + } - if (r.clearingMotionBlur) { - r.motionBlurPxRatio = 1; - } + r.hoverData.draggingEles = true; - // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame - // because a rogue async texture frame would clear needDraw - if (r.textureDrawLastFrame && !textureDraw) { - needDraw[r.NODE] = true; - needDraw[r.SELECT_BOX] = true; - } + var tcol = cy.collection(toTrigger); - var coreStyle = cy.style()._private.coreStyle; + tcol.dirtyCompoundBoundsCache(); + tcol.emit('position drag'); - var zoom = cy.zoom(); - var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom; - var pan = cy.pan(); - var effectivePan = { - x: pan.x, - y: pan.y - }; + r.redrawHint('drag', true); + r.redraw(); + } + } else { + // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant + updateDragDelta(); + } + } - var vp = { - zoom: zoom, - pan: { - x: pan.x, - y: pan.y + // prevent the dragging from triggering text selection on the page + preventDefault = true; } - }; - var prevVp = r.prevViewport; - var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y; - - // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed) - if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) { - r.motionBlurPxRatio = 1; - } - if (forcedPan) { - effectivePan = forcedPan; - } + select[2] = pos[0];select[3] = pos[1]; - // apply pixel ratio + if (preventDefault) { + if (e.stopPropagation) e.stopPropagation(); + if (e.preventDefault) e.preventDefault(); + return false; + } + }, false); - effectiveZoom *= pixelRatio; - effectivePan.x *= pixelRatio; - effectivePan.y *= pixelRatio; + r.registerBinding(window, 'mouseup', function mouseupHandler(e) { + // eslint-disable-line no-undef + var capture = r.hoverData.capture; + if (!capture) { + return; + } + r.hoverData.capture = false; - var eles = r.getCachedZSortedEles(); + var cy = r.cy;var pos = r.projectIntoViewport(e.clientX, e.clientY);var select = r.selection; + var near = r.findNearestElement(pos[0], pos[1], true, false); + var draggedElements = r.dragData.possibleDragElements;var down = r.hoverData.down; + var multSelKeyDown = isMultSelKeyDown(e); - function mbclear(context, x, y, w, h) { - var gco = context.globalCompositeOperation; + if (r.data.bgActivePosistion) { + r.redrawHint('select', true); + r.redraw(); + } - context.globalCompositeOperation = 'destination-out'; - r.fillStyle(context, 255, 255, 255, r.motionBlurTransparency); - context.fillRect(x, y, w, h); + r.hoverData.tapholdCancelled = true; - context.globalCompositeOperation = gco; - } + r.data.bgActivePosistion = undefined; // not active bg now - function setContextTransform(context, clear) { - var ePan, eZoom, w, h; + if (down) { + down.unactivate(); + } - if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) { - ePan = { - x: pan.x * mbPxRatio, - y: pan.y * mbPxRatio + if (r.hoverData.which === 3) { + var cxtEvt = { + originalEvent: e, + type: 'cxttapend', + position: { x: pos[0], y: pos[1] } }; - eZoom = zoom * mbPxRatio; + if (down) { + down.emit(cxtEvt); + } else { + cy.emit(cxtEvt); + } - w = r.canvasWidth * mbPxRatio; - h = r.canvasHeight * mbPxRatio; - } else { - ePan = effectivePan; - eZoom = effectiveZoom; + if (!r.hoverData.cxtDragged) { + var cxtTap = { + originalEvent: e, + type: 'cxttap', + position: { x: pos[0], y: pos[1] } + }; - w = r.canvasWidth; - h = r.canvasHeight; - } + if (down) { + down.emit(cxtTap); + } else { + cy.emit(cxtTap); + } + } - context.setTransform(1, 0, 0, 1, 0, 0); + r.hoverData.cxtDragged = false; + r.hoverData.which = null; + } else if (r.hoverData.which === 1) { - if (clear === 'motionBlur') { - mbclear(context, 0, 0, w, h); - } else if (!forcedContext && (clear === undefined || clear)) { - context.clearRect(0, 0, w, h); - } + // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something + if (down == null && // not mousedown on node + !r.dragData.didDrag // didn't move the node around + && !r.hoverData.selecting // not box selection + && !r.hoverData.dragged // didn't pan + && !isMultSelKeyDown(e)) { - if (!drawAllLayers) { - context.translate(ePan.x, ePan.y); - context.scale(eZoom, eZoom); - } - if (forcedPan) { - context.translate(forcedPan.x, forcedPan.y); - } - if (forcedZoom) { - context.scale(forcedZoom, forcedZoom); - } - } + cy.$(function (ele) { + return ele.selected(); + }).unselect(); - if (!textureDraw) { - r.textureDrawLastFrame = false; - } + if (draggedElements.length > 0) { + r.redrawHint('eles', true); + } - if (textureDraw) { - r.textureDrawLastFrame = true; + r.dragData.possibleDragElements = draggedElements = []; + } - var bb; + triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, { + position: { x: pos[0], y: pos[1] } + }); - if (!r.textureCache) { - r.textureCache = {}; + if (!r.dragData.didDrag // didn't move a node around + && !r.hoverData.dragged // didn't pan + && !r.hoverData.selecting // not box selection + && !r.hoverData.isOverThresholdDrag // didn't move too much + ) { + triggerEvents(down, ['click', 'tap', 'vclick'], e, { + position: { x: pos[0], y: pos[1] } + }); + } - bb = r.textureCache.bb = cy.mutableElements().boundingBox(); + // Single selection + if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) { + if (near != null && near._private.selectable) { - r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER]; + if (r.hoverData.dragging) { + // if panning, don't change selection state + } else if (cy.selectionType() === 'additive' || multSelKeyDown) { + if (near.selected()) { + near.unselect(); + } else { + near.select(); + } + } else { + if (!multSelKeyDown) { + cy.$(':selected').unmerge(near).unselect(); + near.select(); + } + } - var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER]; + r.redrawHint('eles', true); + } + } - cxt.setTransform(1, 0, 0, 1, 0, 0); - cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult); + if (r.hoverData.selecting) { + var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3])); - r.render({ - forcedContext: cxt, - drawOnlyNodeLayer: true, - forcedPxRatio: pixelRatio * r.textureMult - }); + r.redrawHint('select', true); - var vp = r.textureCache.viewport = { - zoom: cy.zoom(), - pan: cy.pan(), - width: r.canvasWidth, - height: r.canvasHeight - }; + if (box.length > 0) { + r.redrawHint('eles', true); + } - vp.mpan = { - x: (0 - vp.pan.x) / vp.zoom, - y: (0 - vp.pan.y) / vp.zoom - }; - } + cy.emit('boxend'); - needDraw[r.DRAG] = false; - needDraw[r.NODE] = false; + var eleWouldBeSelected = function eleWouldBeSelected(ele) { + return ele.selectable() && !ele.selected(); + }; - var context = data.contexts[r.NODE]; + if (cy.selectionType() === 'additive') { + box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect'); + } else { + if (!multSelKeyDown) { + cy.$(':selected').unmerge(box).unselect(); + } - var texture = r.textureCache.texture; - var vp = r.textureCache.viewport; - bb = r.textureCache.bb; + box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect'); + } - context.setTransform(1, 0, 0, 1, 0, 0); + // always need redraw in case eles unselectable + r.redraw(); + } - if (motionBlur) { - mbclear(context, 0, 0, vp.width, vp.height); - } else { - context.clearRect(0, 0, vp.width, vp.height); - } + // Cancel drag pan + if (r.hoverData.dragging) { + r.hoverData.dragging = false; - var outsideBgColor = coreStyle['outside-texture-bg-color'].value; - var outsideBgOpacity = coreStyle['outside-texture-bg-opacity'].value; - r.fillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity); - context.fillRect(0, 0, vp.width, vp.height); + r.redrawHint('select', true); + r.redrawHint('eles', true); - var zoom = cy.zoom(); + r.redraw(); + } - setContextTransform(context, false); + if (!select[4]) { + r.redrawHint('drag', true); + r.redrawHint('eles', true); - context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio); - context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio); - } else if (r.textureOnViewport && !forcedContext) { - // clear the cache since we don't need it - r.textureCache = null; - } + var downWasGrabbed = down && down.grabbed(); - var extent = cy.extent(); - var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles; - var hideEdges = r.hideEdgesOnViewport && vpManip; + freeDraggedElements(draggedElements); - var needMbClear = []; + if (downWasGrabbed) { + down.emit('free'); + } + } + } // else not right mouse - needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur; - if (needMbClear[r.NODE]) { - r.clearedForMotionBlur[r.NODE] = true; - } + select[4] = 0;r.hoverData.down = null; - needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur; - if (needMbClear[r.DRAG]) { - r.clearedForMotionBlur[r.DRAG] = true; - } + r.hoverData.cxtStarted = false; + r.hoverData.draggingEles = false; + r.hoverData.selecting = false; + r.hoverData.isOverThresholdDrag = false; + r.dragData.didDrag = false; + r.hoverData.dragged = false; + r.hoverData.dragDelta = []; + r.hoverData.mdownPos = null; + r.hoverData.mdownGPos = null; + }, false); - if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) { - var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1; - var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]); - var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined; + var wheelHandler = function wheelHandler(e) { - setContextTransform(context, clear); + if (r.scrollingPage) { + return; + } // while scrolling, ignore wheel-to-zoom - if (hideEdges) { - r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent); - } else { - r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent); - } + var cy = r.cy; + var pos = r.projectIntoViewport(e.clientX, e.clientY); + var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y]; - if (r.debug) { - r.drawDebugPoints(context, eles.nondrag); + if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) { + // if pan dragging or cxt dragging, wheel movements make no zoom + e.preventDefault(); + return; } - if (!drawAllLayers && !motionBlur) { - needDraw[r.NODE] = false; - } - } + if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) { + e.preventDefault(); - if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) { - var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1; - var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]); + r.data.wheelZooming = true; + clearTimeout(r.data.wheelTimeout); + r.data.wheelTimeout = setTimeout(function () { + r.data.wheelZooming = false; - setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined); + r.redrawHint('eles', true); + r.redraw(); + }, 150); - if (hideEdges) { - r.drawCachedNodes(context, eles.drag, pixelRatio, extent); - } else { - r.drawCachedElements(context, eles.drag, pixelRatio, extent); - } + var diff; - if (r.debug) { - r.drawDebugPoints(context, eles.drag); - } + if (e.deltaY != null) { + diff = e.deltaY / -250; + } else if (e.wheelDeltaY != null) { + diff = e.wheelDeltaY / 1000; + } else { + diff = e.wheelDelta / 1000; + } - if (!drawAllLayers && !motionBlur) { - needDraw[r.DRAG] = false; - } - } + diff = diff * r.wheelSensitivity; - if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) { - var context = forcedContext || data.contexts[r.SELECT_BOX]; + var needsWheelFix = e.deltaMode === 1; + if (needsWheelFix) { + // fixes slow wheel events on ff/linux and ff/windows + diff *= 33; + } - setContextTransform(context); + cy.zoom({ + level: cy.zoom() * Math.pow(10, diff), + renderedPosition: { x: rpos[0], y: rpos[1] } + }); + } + }; - if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) { - var zoom = r.cy.zoom(); - var borderWidth = coreStyle['selection-box-border-width'].value / zoom; + // Functions to help with whether mouse wheel should trigger zooming + // -- + r.registerBinding(r.container, 'wheel', wheelHandler, true); - context.lineWidth = borderWidth; - context.fillStyle = 'rgba(' + coreStyle['selection-box-color'].value[0] + ',' + coreStyle['selection-box-color'].value[1] + ',' + coreStyle['selection-box-color'].value[2] + ',' + coreStyle['selection-box-opacity'].value + ')'; + // disable nonstandard wheel events + // r.registerBinding(r.container, 'mousewheel', wheelHandler, true); + // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true); + // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox - context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]); + r.registerBinding(window, 'scroll', function scrollHandler(e) { + // eslint-disable-line no-undef + r.scrollingPage = true; - if (borderWidth > 0) { - context.strokeStyle = 'rgba(' + coreStyle['selection-box-border-color'].value[0] + ',' + coreStyle['selection-box-border-color'].value[1] + ',' + coreStyle['selection-box-border-color'].value[2] + ',' + coreStyle['selection-box-opacity'].value + ')'; + clearTimeout(r.scrollingPageTimeout); + r.scrollingPageTimeout = setTimeout(function () { + r.scrollingPage = false; + }, 250); + }, true); - context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]); - } - } + // Functions to help with handling mouseout/mouseover on the Cytoscape container + // Handle mouseout on Cytoscape container + r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) { + var pos = r.projectIntoViewport(e.clientX, e.clientY); - if (data.bgActivePosistion && !r.hoverData.selecting) { - var zoom = r.cy.zoom(); - var pos = data.bgActivePosistion; + r.cy.emit({ + originalEvent: e, + type: 'mouseout', + position: { x: pos[0], y: pos[1] } + }); + }, false); - context.fillStyle = 'rgba(' + coreStyle['active-bg-color'].value[0] + ',' + coreStyle['active-bg-color'].value[1] + ',' + coreStyle['active-bg-color'].value[2] + ',' + coreStyle['active-bg-opacity'].value + ')'; + r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) { + var pos = r.projectIntoViewport(e.clientX, e.clientY); - context.beginPath(); - context.arc(pos.x, pos.y, coreStyle['active-bg-size'].pfValue / zoom, 0, 2 * Math.PI); - context.fill(); - } + r.cy.emit({ + originalEvent: e, + type: 'mouseover', + position: { x: pos[0], y: pos[1] } + }); + }, false); - var timeToRender = r.lastRedrawTime; - if (r.showFps && timeToRender) { - timeToRender = Math.round(timeToRender); - var fps = Math.round(1000 / timeToRender); + var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom + var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom + var center1, modelCenter1; // center point on start pinch to zoom + var offsetLeft, offsetTop; + var containerWidth, containerHeight; + var twoFingersStartInside; - context.setTransform(1, 0, 0, 1, 0, 0); + var distance = function distance(x1, y1, x2, y2) { + return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + }; - context.fillStyle = 'rgba(255, 0, 0, 0.75)'; - context.strokeStyle = 'rgba(255, 0, 0, 0.75)'; - context.lineWidth = 1; - context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20); + var distanceSq = function distanceSq(x1, y1, x2, y2) { + return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); + }; - var maxFps = 60; - context.strokeRect(0, 30, 250, 20); - context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20); + var touchstartHandler; + r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) { + if (!eventInContainer(e)) { + return; } - if (!drawAllLayers) { - needDraw[r.SELECT_BOX] = false; - } - } + r.touchData.capture = true; + r.data.bgActivePosistion = undefined; - // motionblur: blit rendered blurry frames - if (motionBlur && mbPxRatio !== 1) { - var cxtNode = data.contexts[r.NODE]; - var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE]; + var cy = r.cy; + var now = r.touchData.now; + var earlier = r.touchData.earlier; - var cxtDrag = data.contexts[r.DRAG]; - var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]; + if (e.touches[0]) { + var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);now[0] = pos[0];now[1] = pos[1]; + } + if (e.touches[1]) { + var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);now[2] = pos[0];now[3] = pos[1]; + } + if (e.touches[2]) { + var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);now[4] = pos[0];now[5] = pos[1]; + } - var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) { - cxt.setTransform(1, 0, 0, 1, 0, 0); + // record starting points for pinch-to-zoom + if (e.touches[1]) { - if (needClear || !motionBlurFadeEffect) { - cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight); - } else { - mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight); - } + freeDraggedElements(r.dragData.touchDragEles); - var pxr = mbPxRatio; + var offsets = r.findContainerClientCoords(); + offsetLeft = offsets[0]; + offsetTop = offsets[1]; + containerWidth = offsets[2]; + containerHeight = offsets[3]; - cxt.drawImage(txt, // img - 0, 0, // sx, sy - r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh - 0, 0, // x, y - r.canvasWidth, r.canvasHeight // w, h - ); - }; + f1x1 = e.touches[0].clientX - offsetLeft; + f1y1 = e.touches[0].clientY - offsetTop; - if (needDraw[r.NODE] || needMbClear[r.NODE]) { - drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]); - needDraw[r.NODE] = false; - } + f2x1 = e.touches[1].clientX - offsetLeft; + f2y1 = e.touches[1].clientY - offsetTop; - if (needDraw[r.DRAG] || needMbClear[r.DRAG]) { - drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]); - needDraw[r.DRAG] = false; - } - } + twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight; - r.prevViewport = vp; + var pan = cy.pan(); + var zoom = cy.zoom(); - if (r.clearingMotionBlur) { - r.clearingMotionBlur = false; - r.motionBlurCleared = true; - r.motionBlur = true; - } + distance1 = distance(f1x1, f1y1, f2x1, f2y1); + distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1); + center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2]; + modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; - if (motionBlur) { - r.motionBlurTimeout = setTimeout(function () { - r.motionBlurTimeout = null; + // consider context tap + var cxtDistThreshold = 200; + var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold; + if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) { - r.clearedForMotionBlur[r.NODE] = false; - r.clearedForMotionBlur[r.DRAG] = false; - r.motionBlur = false; - r.clearingMotionBlur = !textureDraw; - r.mbFrames = 0; + var near1 = r.findNearestElement(now[0], now[1], true, true); + var near2 = r.findNearestElement(now[2], now[3], true, true); - needDraw[r.NODE] = true; - needDraw[r.DRAG] = true; + if (near1 && near1.isNode()) { + near1.activate().emit({ + originalEvent: e, + type: 'cxttapstart', + position: { x: now[0], y: now[1] } + }); + r.touchData.start = near1; + } else if (near2 && near2.isNode()) { + near2.activate().emit({ + originalEvent: e, + type: 'cxttapstart', + position: { x: now[0], y: now[1] } + }); + r.touchData.start = near2; + } else { + cy.emit({ + originalEvent: e, + type: 'cxttapstart', + position: { x: now[0], y: now[1] } + }); + } - r.redraw(); - }, motionBlurDelay); - } + if (r.touchData.start) { + r.touchData.start._private.grabbed = false; + } + r.touchData.cxt = true; + r.touchData.cxtDragged = false; + r.data.bgActivePosistion = undefined; - if (!forcedContext) { - cy.emit('render'); - } -}; + r.redraw(); + return; + } + } -module.exports = CRp; + if (e.touches[2]) { + // ignore + } else if (e.touches[1]) { + // ignore + } else if (e.touches[0]) { + var nears = r.findNearestElements(now[0], now[1], true, true); + var near = nears[0]; -/***/ }), -/* 108 */ -/***/ (function(module, exports, __webpack_require__) { + if (near != null) { + near.activate(); -"use strict"; + r.touchData.start = near; + r.touchData.starts = nears; + if (r.nodeIsGrabbable(near)) { -var math = __webpack_require__(2); + var draggedEles = r.dragData.touchDragEles = []; + var selectedNodes = null; -var CRp = {}; + r.redrawHint('eles', true); + r.redrawHint('drag', true); -// @O Polygon drawing -CRp.drawPolygonPath = function (context, x, y, width, height, points) { + if (near.selected()) { + // reset drag elements, since near will be added again - var halfW = width / 2; - var halfH = height / 2; + selectedNodes = cy.$(function (ele) { + return ele.selected() && r.nodeIsGrabbable(ele); + }); - if (context.beginPath) { - context.beginPath(); - } + addNodesToDrag(selectedNodes, { addToList: draggedEles }); + } else { + addNodeToDrag(near, { addToList: draggedEles }); + } - context.moveTo(x + halfW * points[0], y + halfH * points[1]); + setGrabTarget(near); - for (var i = 1; i < points.length / 2; i++) { - context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]); - } + var makeEvent = function makeEvent(type) { + return { + originalEvent: e, + type: type, + position: { x: now[0], y: now[1] } + }; + }; - context.closePath(); -}; + near.emit(makeEvent('grabon')); -// Round rectangle drawing -CRp.drawRoundRectanglePath = function (context, x, y, width, height) { + if (selectedNodes) { + selectedNodes.forEach(function (n) { + n.emit(makeEvent('grab')); + }); + } else { + near.emit(makeEvent('grab')); + } + } + } - var halfWidth = width / 2; - var halfHeight = height / 2; - var cornerRadius = math.getRoundRectangleRadius(width, height); + triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, { + position: { x: now[0], y: now[1] } + }); - if (context.beginPath) { - context.beginPath(); - } + if (near == null) { + r.data.bgActivePosistion = { + x: pos[0], + y: pos[1] + }; - // Start at top middle - context.moveTo(x, y - halfHeight); - // Arc from middle top to right side - context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); - // Arc from right side to bottom - context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); - // Arc from bottom to left side - context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); - // Arc from left side to topBorder - context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); - // Join line - context.lineTo(x, y - halfHeight); + r.redrawHint('select', true); + r.redraw(); + } - context.closePath(); -}; + // Tap, taphold + // ----- -CRp.drawBottomRoundRectanglePath = function (context, x, y, width, height) { + r.touchData.singleTouchMoved = false; + r.touchData.singleTouchStartTime = +new Date(); - var halfWidth = width / 2; - var halfHeight = height / 2; - var cornerRadius = math.getRoundRectangleRadius(width, height); + clearTimeout(r.touchData.tapholdTimeout); + r.touchData.tapholdTimeout = setTimeout(function () { + if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect + && !r.touchData.selecting // box selection shouldn't allow taphold through + ) { + triggerEvents(r.touchData.start, ['taphold'], e, { + position: { x: now[0], y: now[1] } + }); - if (context.beginPath) { - context.beginPath(); - } + if (!r.touchData.start) { + cy.$(':selected').unselect(); + } + } + }, r.tapholdDuration); + } - // Start at top middle - context.moveTo(x, y - halfHeight); - context.lineTo(x + halfWidth, y - halfHeight); - context.lineTo(x + halfWidth, y); + if (e.touches.length >= 1) { + var sPos = r.touchData.startPosition = []; - context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); - context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); + for (var i = 0; i < now.length; i++) { + sPos[i] = earlier[i] = now[i]; + } - context.lineTo(x - halfWidth, y - halfHeight); - context.lineTo(x, y - halfHeight); + var touch0 = e.touches[0]; - context.closePath(); -}; + r.touchData.startGPosition = [touch0.clientX, touch0.clientY]; + } + }, false); -CRp.drawCutRectanglePath = function (context, x, y, width, height) { + var touchmoveHandler; + r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) { + // eslint-disable-line no-undef + var capture = r.touchData.capture; - var halfWidth = width / 2; - var halfHeight = height / 2; - var cornerLength = math.getCutRectangleCornerLength(); + if (!capture && !eventInContainer(e)) { + return; + } - if (context.beginPath) { - context.beginPath(); - } + var select = r.selection; + var cy = r.cy; + var now = r.touchData.now; + var earlier = r.touchData.earlier; + var zoom = cy.zoom(); - context.moveTo(x - halfWidth + cornerLength, y - halfHeight); + if (e.touches[0]) { + var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);now[0] = pos[0];now[1] = pos[1]; + } + if (e.touches[1]) { + var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);now[2] = pos[0];now[3] = pos[1]; + } + if (e.touches[2]) { + var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);now[4] = pos[0];now[5] = pos[1]; + } - context.lineTo(x + halfWidth - cornerLength, y - halfHeight); - context.lineTo(x + halfWidth, y - halfHeight + cornerLength); - context.lineTo(x + halfWidth, y + halfHeight - cornerLength); - context.lineTo(x + halfWidth - cornerLength, y + halfHeight); - context.lineTo(x - halfWidth + cornerLength, y + halfHeight); - context.lineTo(x - halfWidth, y + halfHeight - cornerLength); - context.lineTo(x - halfWidth, y - halfHeight + cornerLength); + var startGPos = r.touchData.startGPosition; + var isOverThresholdDrag; - context.closePath(); -}; + if (capture && e.touches[0] && startGPos) { + var disp = [];for (var j = 0; j < now.length; j++) { + disp[j] = now[j] - earlier[j]; + } + var dx = e.touches[0].clientX - startGPos[0]; + var dx2 = dx * dx; + var dy = e.touches[0].clientY - startGPos[1]; + var dy2 = dy * dy; + var dist2 = dx2 + dy2; -CRp.drawBarrelPath = function (context, x, y, width, height) { + isOverThresholdDrag = dist2 >= r.touchTapThreshold2; + } - var halfWidth = width / 2; - var halfHeight = height / 2; + // context swipe cancelling + if (capture && r.touchData.cxt) { + e.preventDefault(); - var xBegin = x - halfWidth; - var xEnd = x + halfWidth; - var yBegin = y - halfHeight; - var yEnd = y + halfHeight; + var f1x2 = e.touches[0].clientX - offsetLeft, + f1y2 = e.touches[0].clientY - offsetTop; + var f2x2 = e.touches[1].clientX - offsetLeft, + f2y2 = e.touches[1].clientY - offsetTop; + // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 ); + var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2); + var factorSq = distance2Sq / distance1Sq; - var barrelCurveConstants = math.getBarrelCurveConstants(width, height); - var wOffset = barrelCurveConstants.widthOffset; - var hOffset = barrelCurveConstants.heightOffset; - var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset; + var distThreshold = 150; + var distThresholdSq = distThreshold * distThreshold; + var factorThreshold = 1.5; + var factorThresholdSq = factorThreshold * factorThreshold; - if (context.beginPath) { - context.beginPath(); - } + // cancel ctx gestures if the distance b/t the fingers increases + if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) { + r.touchData.cxt = false; - context.moveTo(xBegin, yBegin + hOffset); + r.data.bgActivePosistion = undefined; - context.lineTo(xBegin, yEnd - hOffset); - context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd); + r.redrawHint('select', true); - context.lineTo(xEnd - wOffset, yEnd); - context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset); + var cxtEvt = { + originalEvent: e, + type: 'cxttapend', + position: { x: now[0], y: now[1] } + }; - context.lineTo(xEnd, yBegin + hOffset); - context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin); + if (r.touchData.start) { + r.touchData.start.unactivate().emit(cxtEvt); - context.lineTo(xBegin + wOffset, yBegin); - context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset); + r.touchData.start = null; + } else { + cy.emit(cxtEvt); + } + } + } - context.closePath(); -}; + // context swipe + if (capture && r.touchData.cxt) { + var cxtEvt = { + originalEvent: e, + type: 'cxtdrag', + position: { x: now[0], y: now[1] } + }; + r.data.bgActivePosistion = undefined; + r.redrawHint('select', true); -var sin0 = Math.sin(0); -var cos0 = Math.cos(0); + if (r.touchData.start) { + r.touchData.start.emit(cxtEvt); + } else { + cy.emit(cxtEvt); + } -var sin = {}; -var cos = {}; + if (r.touchData.start) { + r.touchData.start._private.grabbed = false; + } + r.touchData.cxtDragged = true; -var ellipseStepSize = Math.PI / 40; + var near = r.findNearestElement(now[0], now[1], true, true); -for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) { - sin[i] = Math.sin(i); - cos[i] = Math.cos(i); -} + if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) { -CRp.drawEllipsePath = function (context, centerX, centerY, width, height) { - if (context.beginPath) { - context.beginPath(); - } + if (r.touchData.cxtOver) { + r.touchData.cxtOver.emit({ + originalEvent: e, + type: 'cxtdragout', + position: { x: now[0], y: now[1] } + }); + } - if (context.ellipse) { - context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI); - } else { - var xPos, yPos; - var rw = width / 2; - var rh = height / 2; - for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) { - xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0; - yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0; + r.touchData.cxtOver = near; - if (i === 0) { - context.moveTo(xPos, yPos); - } else { - context.lineTo(xPos, yPos); + if (near) { + near.emit({ + originalEvent: e, + type: 'cxtdragover', + position: { x: now[0], y: now[1] } + }); + } } - } - } - context.closePath(); -}; + // box selection + } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) { + e.preventDefault(); -module.exports = CRp; + r.data.bgActivePosistion = undefined; -/***/ }), -/* 109 */ -/***/ (function(module, exports, __webpack_require__) { + this.lastThreeTouch = +new Date(); -"use strict"; + if (!r.touchData.selecting) { + cy.emit('boxstart'); + } + r.touchData.selecting = true; -var math = __webpack_require__(2); -var util = __webpack_require__(1); -var Heap = __webpack_require__(8); -var defs = __webpack_require__(16); + r.redrawHint('select', true); -var minTxrH = 25; // the size of the texture cache for small height eles (special case) -var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up -var minLvl = -4; // when scaling smaller than that we don't need to re-render -var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful) -var maxZoom = 3.99; // beyond this zoom level, layered textures are not used -var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps -var defTxrWidth = 1024; // default/minimum texture width -var maxTxrW = 1024; // the maximum width of a texture -var maxTxrH = 1024; // the maximum height of a texture -var minUtility = 0.5; // if usage of texture is less than this, it is retired -var maxFullness = 0.8; // fullness of texture after which queue removal is checked -var maxFullnessChecks = 10; // dequeued after this many checks -var allowEdgeTxrCaching = false; // whether edges can be cached as textures (TODO maybe better on if webgl supported?) -var allowParentTxrCaching = false; // whether parent nodes can be cached as textures (TODO maybe better on if webgl supported?) -var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame -var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time -var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing -var deqFastCost = 0.9; // % of frame time to be used when >60fps -var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile -var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch + if (!select || select.length === 0 || select[0] === undefined) { + select[0] = (now[0] + now[2] + now[4]) / 3; + select[1] = (now[1] + now[3] + now[5]) / 3; + select[2] = (now[0] + now[2] + now[4]) / 3 + 1; + select[3] = (now[1] + now[3] + now[5]) / 3 + 1; + } else { + select[2] = (now[0] + now[2] + now[4]) / 3; + select[3] = (now[1] + now[3] + now[5]) / 3; + } -var getTxrReasons = { - dequeue: 'dequeue', - downscale: 'downscale', - highQuality: 'highQuality' -}; + select[4] = 1; + r.touchData.selecting = true; -var ElementTextureCache = function ElementTextureCache(renderer) { - var self = this; + r.redraw(); - self.renderer = renderer; - self.onDequeues = []; + // pinch to zoom + } else if (capture && e.touches[1] && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) { + // two fingers => pinch to zoom + e.preventDefault(); - self.setupDequeueing(); -}; + r.data.bgActivePosistion = undefined; + r.redrawHint('select', true); -var ETCp = ElementTextureCache.prototype; + var draggedEles = r.dragData.touchDragEles; + if (draggedEles) { + r.redrawHint('drag', true); -ETCp.reasons = getTxrReasons; + for (var i = 0; i < draggedEles.length; i++) { + var de_p = draggedEles[i]._private; -// the list of textures in which new subtextures for elements can be placed -ETCp.getTextureQueue = function (txrH) { - var self = this; - self.eleImgCaches = self.eleImgCaches || {}; + de_p.grabbed = false; + de_p.rscratch.inDragLayer = false; + } + } - return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || []; -}; + // (x2, y2) for fingers 1 and 2 + var f1x2 = e.touches[0].clientX - offsetLeft, + f1y2 = e.touches[0].clientY - offsetTop; + var f2x2 = e.touches[1].clientX - offsetLeft, + f2y2 = e.touches[1].clientY - offsetTop; -// the list of usused textures which can be recycled (in use in texture queue) -ETCp.getRetiredTextureQueue = function (txrH) { - var self = this; + var distance2 = distance(f1x2, f1y2, f2x2, f2y2); + // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 ); + // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq ); + var factor = distance2 / distance1; - var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {}; - var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || []; + if (twoFingersStartInside) { + // delta finger1 + var df1x = f1x2 - f1x1; + var df1y = f1y2 - f1y1; - return rtxtrQ; -}; + // delta finger 2 + var df2x = f2x2 - f2x1; + var df2y = f2y2 - f2y1; -// queue of element draw requests at different scale levels -ETCp.getElementQueue = function () { - var self = this; + // translation is the normalised vector of the two fingers movement + // i.e. so pinching cancels out and moving together pans + var tx = (df1x + df2x) / 2; + var ty = (df1y + df2y) / 2; - var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) { - return b.reqs - a.reqs; - }); + // adjust factor by the speed multiplier + // var speed = 1.5; + // if( factor > 1 ){ + // factor = (factor - 1) * speed + 1; + // } else { + // factor = 1 - (1 - factor) * speed; + // } - return q; -}; + // now calculate the zoom + var zoom1 = cy.zoom(); + var zoom2 = zoom1 * factor; + var pan1 = cy.pan(); -// queue of element draw requests at different scale levels (element id lookup) -ETCp.getElementIdToQueue = function () { - var self = this; + // the model center point converted to the current rendered pos + var ctrx = modelCenter1[0] * zoom1 + pan1.x; + var ctry = modelCenter1[1] * zoom1 + pan1.y; - var id2q = self.eleIdToCacheQueue = self.eleIdToCacheQueue || {}; + var pan2 = { + x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx, + y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry + }; - return id2q; -}; + // remove dragged eles + if (r.touchData.start && r.touchData.start.active()) { + var draggedEles = r.dragData.touchDragEles; -ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) { - var self = this; - var r = this.renderer; - var rs = ele._private.rscratch; - var zoom = r.cy.zoom(); + freeDraggedElements(draggedEles); - if (bb.w === 0 || bb.h === 0 || !ele.visible()) { - return null; - } + r.redrawHint('drag', true); + r.redrawHint('eles', true); - if (lvl == null) { - lvl = Math.ceil(math.log2(zoom * pxRatio)); - } + r.touchData.start.unactivate().emit('free'); + } + + cy.viewport({ + zoom: zoom2, + pan: pan2, + cancelOnFailedZoom: true + }); + + distance1 = distance2; + f1x1 = f1x2; + f1y1 = f1y2; + f2x1 = f2x2; + f2y1 = f2y2; - if (lvl < minLvl) { - lvl = minLvl; - } else if (zoom >= maxZoom || lvl > maxLvl) { - return null; - } + r.pinching = true; + } - var scale = Math.pow(2, lvl); - var eleScaledH = bb.h * scale; - var eleScaledW = bb.w * scale; - var caches = rs.imgCaches = rs.imgCaches || {}; - var eleCache = caches[lvl]; + // Re-project + if (e.touches[0]) { + var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);now[0] = pos[0];now[1] = pos[1]; + } + if (e.touches[1]) { + var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);now[2] = pos[0];now[3] = pos[1]; + } + if (e.touches[2]) { + var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);now[4] = pos[0];now[5] = pos[1]; + } + } else if (e.touches[0]) { + var start = r.touchData.start; + var last = r.touchData.last; + var near; - if (eleCache) { - return eleCache; - } + if (!r.hoverData.draggingEles && !r.swipePanning) { + near = r.findNearestElement(now[0], now[1], true, true); + } - var txrH; // which texture height this ele belongs to + if (capture && start != null) { + e.preventDefault(); + } - if (eleScaledH <= minTxrH) { - txrH = minTxrH; - } else if (eleScaledH <= txrStepH) { - txrH = txrStepH; - } else { - txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH; - } + // dragging nodes + if (capture && start != null && r.nodeIsDraggable(start)) { - if (eleScaledH > maxTxrH || eleScaledW > maxTxrW || !allowEdgeTxrCaching && ele.isEdge() || !allowParentTxrCaching && ele.isParent()) { - return null; // caching large elements is not efficient - } + if (isOverThresholdDrag) { + // then dragging can happen + var draggedEles = r.dragData.touchDragEles; + var justStartedDrag = !r.dragData.didDrag; - var txrQ = self.getTextureQueue(txrH); + if (justStartedDrag) { + addNodesToDrag(cy.collection(draggedEles), { inDragLayer: true }); + } - // first try the second last one in case it has space at the end - var txr = txrQ[txrQ.length - 2]; + for (var k = 0; k < draggedEles.length; k++) { + var draggedEle = draggedEles[k]; - var addNewTxr = function addNewTxr() { - return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW); - }; + if (r.nodeIsDraggable(draggedEle) && draggedEle.grabbed()) { + r.dragData.didDrag = true; + var dPos = draggedEle.position(); - // try the last one if there is no second last one - if (!txr) { - txr = txrQ[txrQ.length - 1]; - } + if (is.number(disp[0]) && is.number(disp[1])) { + dPos.x += disp[0]; + dPos.y += disp[1]; + } - // if the last one doesn't exist, we need a first one - if (!txr) { - txr = addNewTxr(); - } + if (justStartedDrag) { + r.redrawHint('eles', true); - // if there's no room in the current texture, we need a new one - if (txr.width - txr.usedWidth < eleScaledW) { - txr = addNewTxr(); - } + var dragDelta = r.touchData.dragDelta; - var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale); - var scalableFrom = function scalableFrom(otherCache) { - return otherCache && otherCache.scaledLabelShown === scaledLabelShown; - }; + if (dragDelta && is.number(dragDelta[0]) && is.number(dragDelta[1])) { + dPos.x += dragDelta[0]; + dPos.y += dragDelta[1]; + } + } + } + } - var deqing = reason && reason === getTxrReasons.dequeue; - var highQualityReq = reason && reason === getTxrReasons.highQuality; - var downscaleReq = reason && reason === getTxrReasons.downscale; + var tcol = cy.collection(draggedEles); - var higherCache; // the nearest cache with a higher level - for (var l = lvl + 1; l <= maxLvl; l++) { - var c = caches[l]; + tcol.dirtyCompoundBoundsCache(); + tcol.emit('position drag'); - if (c) { - higherCache = c;break; - } - } + r.hoverData.draggingEles = true; - var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null; + r.redrawHint('drag', true); - var downscale = function downscale() { - txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH); - }; + if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) { - // reset ele area in texture - txr.context.setTransform(1, 0, 0, 1, 0, 0); - txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH); + r.redrawHint('eles', true); + } - if (scalableFrom(oneUpCache)) { - // then we can relatively cheaply rescale the existing image w/o rerendering - downscale(); - } else if (scalableFrom(higherCache)) { - // then use the higher cache for now and queue the next level down - // to cheaply scale towards the smaller level + r.redraw(); + } else { + // otherise keep track of drag delta for later + var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || []; - if (highQualityReq) { - for (var l = higherCache.level; l > lvl; l--) { - oneUpCache = self.getElement(ele, bb, pxRatio, l, getTxrReasons.downscale); + if (dragDelta.length === 0) { + dragDelta.push(disp[0]); + dragDelta.push(disp[1]); + } else { + dragDelta[0] += disp[0]; + dragDelta[1] += disp[1]; + } + } } - downscale(); - } else { - self.queueElement(ele, higherCache.level - 1); + // touchmove + { + triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, { + position: { x: now[0], y: now[1] } + }); - return higherCache; - } - } else { + if ((!start || !start.grabbed()) && near != last) { + if (last) { + last.emit({ originalEvent: e, type: 'tapdragout', position: { x: now[0], y: now[1] } }); + } + if (near) { + near.emit({ originalEvent: e, type: 'tapdragover', position: { x: now[0], y: now[1] } }); + } + } - var lowerCache; // the nearest cache with a lower level - if (!deqing && !highQualityReq && !downscaleReq) { - for (var l = lvl - 1; l >= minLvl; l--) { - var c = caches[l]; + r.touchData.last = near; + } - if (c) { - lowerCache = c;break; + // check to cancel taphold + if (capture) { + for (var i = 0; i < now.length; i++) { + if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) { + + r.touchData.singleTouchMoved = true; + } } } - } - if (scalableFrom(lowerCache)) { - // then use the lower quality cache for now and queue the better one for later - - self.queueElement(ele, lvl); + // panning + if (capture && (start == null || start.isEdge()) && cy.panningEnabled() && cy.userPanningEnabled()) { - return lowerCache; - } + var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts); - txr.context.translate(txr.usedWidth, 0); - txr.context.scale(scale, scale); + if (allowPassthrough) { + e.preventDefault(); - r.drawElement(txr.context, ele, bb, scaledLabelShown); + if (r.swipePanning) { + cy.panBy({ + x: disp[0] * zoom, + y: disp[1] * zoom + }); + } else if (isOverThresholdDrag) { + r.swipePanning = true; - txr.context.scale(1 / scale, 1 / scale); - txr.context.translate(-txr.usedWidth, 0); - } + cy.panBy({ + x: dx * zoom, + y: dy * zoom + }); - eleCache = caches[lvl] = { - ele: ele, - x: txr.usedWidth, - texture: txr, - level: lvl, - scale: scale, - width: eleScaledW, - height: eleScaledH, - scaledLabelShown: scaledLabelShown - }; + if (start) { + start.unactivate(); - txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing); + if (!r.data.bgActivePosistion) { + r.data.bgActivePosistion = math.array2point(r.touchData.startPosition); + } - txr.eleCaches.push(eleCache); + r.redrawHint('select', true); - self.checkTextureFullness(txr); + r.touchData.start = null; + } + } + } - return eleCache; -}; + // Re-project + var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY); + now[0] = pos[0];now[1] = pos[1]; + } + } -ETCp.invalidateElement = function (ele) { - var self = this; - var caches = ele._private.rscratch.imgCaches; + for (var j = 0; j < now.length; j++) { + earlier[j] = now[j]; + } + //r.redraw(); + }, false); - if (caches) { - for (var lvl = minLvl; lvl <= maxLvl; lvl++) { - var cache = caches[lvl]; + var touchcancelHandler; + r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) { + // eslint-disable-line no-undef + var start = r.touchData.start; - if (cache) { - var txr = cache.texture; + r.touchData.capture = false; - // remove space from the texture it belongs to - txr.invalidatedWidth += cache.width; + if (start) { + start.unactivate(); + } + }); - // remove refs with the element - caches[lvl] = null; - util.removeFromArray(txr.eleCaches, cache); + var touchendHandler; + r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) { + // eslint-disable-line no-undef + var start = r.touchData.start; - // remove from queue since the old req was for the old state - self.removeFromQueue(ele); + var capture = r.touchData.capture; - // might have to remove the entire texture if it's not efficiently using its space - self.checkTextureUtility(txr); - } - } - } -}; + if (capture) { + r.touchData.capture = false; -ETCp.checkTextureUtility = function (txr) { - // invalidate all entries in the cache if the cache size is small - if (txr.invalidatedWidth >= minUtility * txr.width) { - this.retireTexture(txr); - } -}; + e.preventDefault(); + } else { + return; + } -ETCp.checkTextureFullness = function (txr) { - // if texture has been mostly filled and passed over several times, remove - // it from the queue so we don't need to waste time looking at it to put new things + var select = r.selection; - var self = this; - var txrQ = self.getTextureQueue(txr.height); + r.swipePanning = false; + r.hoverData.draggingEles = false; - if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) { - util.removeFromArray(txrQ, txr); - } else { - txr.fullnessChecks++; - } -}; + var cy = r.cy; + var zoom = cy.zoom(); + var now = r.touchData.now; + var earlier = r.touchData.earlier; -ETCp.retireTexture = function (txr) { - var self = this; - var txrH = txr.height; - var txrQ = self.getTextureQueue(txrH); + if (e.touches[0]) { + var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);now[0] = pos[0];now[1] = pos[1]; + } + if (e.touches[1]) { + var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);now[2] = pos[0];now[3] = pos[1]; + } + if (e.touches[2]) { + var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);now[4] = pos[0];now[5] = pos[1]; + } - // retire the texture from the active / searchable queue: + if (start) { + start.unactivate(); + } - util.removeFromArray(txrQ, txr); + var ctxTapend; + if (r.touchData.cxt) { + ctxTapend = { + originalEvent: e, + type: 'cxttapend', + position: { x: now[0], y: now[1] } + }; - txr.retired = true; + if (start) { + start.emit(ctxTapend); + } else { + cy.emit(ctxTapend); + } - // remove the refs from the eles to the caches: + if (!r.touchData.cxtDragged) { + var ctxTap = { + originalEvent: e, + type: 'cxttap', + position: { x: now[0], y: now[1] } + }; - var eleCaches = txr.eleCaches; + if (start) { + start.emit(ctxTap); + } else { + cy.emit(ctxTap); + } + } - for (var i = 0; i < eleCaches.length; i++) { - var eleCache = eleCaches[i]; - var ele = eleCache.ele; - var lvl = eleCache.level; - var imgCaches = ele._private.rscratch.imgCaches; + if (r.touchData.start) { + r.touchData.start._private.grabbed = false; + } + r.touchData.cxt = false; + r.touchData.start = null; - if (imgCaches) { - imgCaches[lvl] = null; + r.redraw(); + return; } - } - util.clearArray(eleCaches); + // no more box selection if we don't have three fingers + if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) { + r.touchData.selecting = false; - // add the texture to a retired queue so it can be recycled in future: + var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3])); - var rtxtrQ = self.getRetiredTextureQueue(txrH); + select[0] = undefined; + select[1] = undefined; + select[2] = undefined; + select[3] = undefined; + select[4] = 0; - rtxtrQ.push(txr); -}; + r.redrawHint('select', true); -ETCp.addTexture = function (txrH, minW) { - var self = this; - var txrQ = self.getTextureQueue(txrH); - var txr = {}; + cy.emit('boxend'); - txrQ.push(txr); + var eleWouldBeSelected = function eleWouldBeSelected(ele) { + return ele.selectable() && !ele.selected(); + }; - txr.eleCaches = []; + box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect'); - txr.height = txrH; - txr.width = Math.max(defTxrWidth, minW); - txr.usedWidth = 0; - txr.invalidatedWidth = 0; - txr.fullnessChecks = 0; + if (box.nonempty()) { + r.redrawHint('eles', true); + } - txr.canvas = document.createElement('canvas'); // eslint-disable-line no-undef - txr.canvas.width = txr.width; - txr.canvas.height = txr.height; + r.redraw(); + } - txr.context = txr.canvas.getContext('2d'); + if (start != null) { + start.unactivate(); + } - return txr; -}; + if (e.touches[2]) { + r.data.bgActivePosistion = undefined; + r.redrawHint('select', true); + } else if (e.touches[1]) { + // ignore + } else if (e.touches[0]) { + // ignore -ETCp.recycleTexture = function (txrH, minW) { - var self = this; - var txrQ = self.getTextureQueue(txrH); - var rtxtrQ = self.getRetiredTextureQueue(txrH); + // Last touch released + } else if (!e.touches[0]) { - for (var i = 0; i < rtxtrQ.length; i++) { - var txr = rtxtrQ[i]; + r.data.bgActivePosistion = undefined; + r.redrawHint('select', true); - if (txr.width >= minW) { - txr.retired = false; + var draggedEles = r.dragData.touchDragEles; - txr.usedWidth = 0; - txr.invalidatedWidth = 0; - txr.fullnessChecks = 0; + if (start != null) { - util.clearArray(txr.eleCaches); + var startWasGrabbed = start._private.grabbed; - txr.context.setTransform(1, 0, 0, 1, 0, 0); - txr.context.clearRect(0, 0, txr.width, txr.height); + freeDraggedElements(draggedEles); - util.removeFromArray(rtxtrQ, txr); - txrQ.push(txr); + r.redrawHint('drag', true); + r.redrawHint('eles', true); - return txr; - } - } -}; + if (startWasGrabbed) { + start.emit('free'); + } -ETCp.queueElement = function (ele, lvl) { - var self = this; - var q = self.getElementQueue(); - var id2q = self.getElementIdToQueue(); - var id = ele.id(); - var existingReq = id2q[id]; + triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, { + position: { x: now[0], y: now[1] } + }); - if (existingReq) { - // use the max lvl b/c in between lvls are cheap to make - existingReq.level = Math.max(existingReq.level, lvl); - existingReq.reqs++; + start.unactivate(); - q.updateItem(existingReq); - } else { - var req = { - ele: ele, - level: lvl, - reqs: 1 - }; + r.touchData.start = null; + } else { + var near = r.findNearestElement(now[0], now[1], true, true); - q.push(req); + triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, { + position: { x: now[0], y: now[1] } + }); + } - id2q[id] = req; - } -}; + var dx = r.touchData.startPosition[0] - now[0]; + var dx2 = dx * dx; + var dy = r.touchData.startPosition[1] - now[1]; + var dy2 = dy * dy; + var dist2 = dx2 + dy2; + var rdist2 = dist2 * zoom * zoom; -ETCp.dequeue = function (pxRatio /*, extent*/) { - var self = this; - var q = self.getElementQueue(); - var id2q = self.getElementIdToQueue(); - var dequeued = []; + // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance + if (start != null && !r.dragData.didDrag // didn't drag nodes around + && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection + ) { - for (var i = 0; i < maxDeqSize; i++) { - if (q.size() > 0) { - var req = q.pop(); - var ele = req.ele; - var caches = ele._private.rscratch.imgCaches; + if (cy.selectionType() === 'single') { + cy.$(':selected').unmerge(start).unselect(); + start.select(); + } else { + if (start.selected()) { + start.unselect(); + } else { + start.select(); + } + } - // dequeueing isn't necessary when an existing cache exists - if (caches[req.level] != null) { - continue; + r.redrawHint('eles', true); + } + + // Tap event, roughly same as mouse click event for touch + if (!r.touchData.singleTouchMoved) { + triggerEvents(start, ['tap', 'vclick'], e, { + position: { x: now[0], y: now[1] } + }); } - id2q[ele.id()] = null; + r.touchData.singleTouchMoved = true; + } - dequeued.push(req); + for (var j = 0; j < now.length; j++) { + earlier[j] = now[j]; + } - var bb = ele.boundingBox(); + r.dragData.didDrag = false; // reset for next mousedown - self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue); - } else { - break; + if (e.touches.length === 0) { + r.touchData.dragDelta = []; + r.touchData.startPosition = null; + r.touchData.startGPosition = null; } - } - return dequeued; -}; + if (e.touches.length < 2) { + r.pinching = false; + r.redrawHint('eles', true); + r.redraw(); + } -ETCp.removeFromQueue = function (ele) { - var self = this; - var q = self.getElementQueue(); - var id2q = self.getElementIdToQueue(); - var req = id2q[ele.id()]; + //r.redraw(); + }, false); - if (req != null) { - // bring to front of queue - req.reqs = util.MAX_INT; - q.updateItem(req); + // fallback compatibility layer for ms pointer events + if (typeof TouchEvent === 'undefined') { - q.pop(); // remove from queue + var pointers = []; - id2q[ele.id()] = null; // remove from lookup map - } -}; + var makeTouch = function makeTouch(e) { + return { + clientX: e.clientX, + clientY: e.clientY, + force: 1, + identifier: e.pointerId, + pageX: e.pageX, + pageY: e.pageY, + radiusX: e.width / 2, + radiusY: e.height / 2, + screenX: e.screenX, + screenY: e.screenY, + target: e.target + }; + }; -ETCp.onDequeue = function (fn) { - this.onDequeues.push(fn); -}; -ETCp.offDequeue = function (fn) { - util.removeFromArray(this.onDequeues, fn); -}; + var makePointer = function makePointer(e) { + return { + event: e, + touch: makeTouch(e) + }; + }; -ETCp.setupDequeueing = defs.setupDequeueing({ - deqRedrawThreshold: deqRedrawThreshold, - deqCost: deqCost, - deqAvgCost: deqAvgCost, - deqNoDrawCost: deqNoDrawCost, - deqFastCost: deqFastCost, - deq: function deq(self, pxRatio, extent) { - return self.dequeue(pxRatio, extent); - }, - onDeqd: function onDeqd(self, deqd) { - for (var i = 0; i < self.onDequeues.length; i++) { - var fn = self.onDequeues[i]; + var addPointer = function addPointer(e) { + pointers.push(makePointer(e)); + }; - fn(deqd); - } - }, - shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) { - for (var i = 0; i < deqd.length; i++) { - var bb = deqd[i].ele.boundingBox(); + var removePointer = function removePointer(e) { + for (var i = 0; i < pointers.length; i++) { + var p = pointers[i]; - if (math.boundingBoxesIntersect(bb, extent)) { - return true; + if (p.event.pointerId === e.pointerId) { + pointers.splice(i, 1); + return; + } } - } + }; - return false; - }, - priority: function priority(self) { - return self.renderer.beforeRenderPriorities.eleTxrDeq; - } -}); + var updatePointer = function updatePointer(e) { + var p = pointers.filter(function (p) { + return p.event.pointerId === e.pointerId; + })[0]; -module.exports = ElementTextureCache; + p.event = e; + p.touch = makeTouch(e); + }; -/***/ }), -/* 110 */ -/***/ (function(module, exports, __webpack_require__) { + var addTouchesToEvent = function addTouchesToEvent(e) { + e.touches = pointers.map(function (p) { + return p.touch; + }); + }; -"use strict"; + var pointerIsMouse = function pointerIsMouse(e) { + return e.pointerType === 'mouse' || e.pointerType === 4; + }; + r.registerBinding(r.container, 'pointerdown', function (e) { + if (pointerIsMouse(e)) { + return; + } // mouse already handled -var is = __webpack_require__(0); + e.preventDefault(); -var CRp = {}; + addPointer(e); -CRp.createBuffer = function (w, h) { - var buffer = document.createElement('canvas'); // eslint-disable-line no-undef - buffer.width = w; - buffer.height = h; + addTouchesToEvent(e); + touchstartHandler(e); + }); - return [buffer, buffer.getContext('2d')]; -}; + r.registerBinding(r.container, 'pointerup', function (e) { + if (pointerIsMouse(e)) { + return; + } // mouse already handled -CRp.bufferCanvasImage = function (options) { - var cy = this.cy; - var eles = cy.mutableElements(); - var bb = eles.boundingBox(); - var ctrRect = this.findContainerClientCoords(); - var width = options.full ? Math.ceil(bb.w) : ctrRect[2]; - var height = options.full ? Math.ceil(bb.h) : ctrRect[3]; - var specdMaxDims = is.number(options.maxWidth) || is.number(options.maxHeight); - var pxRatio = this.getPixelRatio(); - var scale = 1; + removePointer(e); - if (options.scale !== undefined) { - width *= options.scale; - height *= options.scale; + addTouchesToEvent(e); + touchendHandler(e); + }); - scale = options.scale; - } else if (specdMaxDims) { - var maxScaleW = Infinity; - var maxScaleH = Infinity; + r.registerBinding(r.container, 'pointercancel', function (e) { + if (pointerIsMouse(e)) { + return; + } // mouse already handled - if (is.number(options.maxWidth)) { - maxScaleW = scale * options.maxWidth / width; - } + removePointer(e); - if (is.number(options.maxHeight)) { - maxScaleH = scale * options.maxHeight / height; - } + addTouchesToEvent(e); + touchcancelHandler(e); + }); - scale = Math.min(maxScaleW, maxScaleH); + r.registerBinding(r.container, 'pointermove', function (e) { + if (pointerIsMouse(e)) { + return; + } // mouse already handled - width *= scale; - height *= scale; - } + e.preventDefault(); - if (!specdMaxDims) { - width *= pxRatio; - height *= pxRatio; - scale *= pxRatio; + updatePointer(e); + + addTouchesToEvent(e); + touchmoveHandler(e); + }); } +}; - var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef +module.exports = BRp; - buffCanvas.width = width; - buffCanvas.height = height; +/***/ }), +/* 121 */ +/***/ (function(module, exports, __webpack_require__) { - buffCanvas.style.width = width + 'px'; - buffCanvas.style.height = height + 'px'; +"use strict"; - var buffCxt = buffCanvas.getContext('2d'); - // Rasterize the layers, but only if container has nonzero size - if (width > 0 && height > 0) { +var math = __webpack_require__(2); - buffCxt.clearRect(0, 0, width, height); +var BRp = {}; - buffCxt.globalCompositeOperation = 'source-over'; +BRp.generatePolygon = function (name, points) { + return this.nodeShapes[name] = { + renderer: this, + + name: name, + + points: points, + + draw: function draw(context, centerX, centerY, width, height) { + this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points); + }, + + intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { + return math.polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding); + }, + + checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { + return math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding); + } + }; +}; + +BRp.generateEllipse = function () { + return this.nodeShapes['ellipse'] = { + renderer: this, - var zsortedEles = this.getCachedZSortedEles(); + name: 'ellipse', - if (options.full) { - // draw the full bounds of the graph - buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale); - buffCxt.scale(scale, scale); + draw: function draw(context, centerX, centerY, width, height) { + this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); + }, - this.drawElements(buffCxt, zsortedEles); + intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { + return math.intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding); + }, - buffCxt.scale(1 / scale, 1 / scale); - buffCxt.translate(bb.x1 * scale, bb.y1 * scale); - } else { - // draw the current view - var pan = cy.pan(); + checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { + return math.checkInEllipse(x, y, width, height, centerX, centerY, padding); + } + }; +}; - var translation = { - x: pan.x * scale, - y: pan.y * scale - }; +BRp.generateRoundRectangle = function () { + return this.nodeShapes['roundrectangle'] = { + renderer: this, - scale *= cy.zoom(); + name: 'roundrectangle', - buffCxt.translate(translation.x, translation.y); - buffCxt.scale(scale, scale); + points: math.generateUnitNgonPointsFitToSquare(4, 0), - this.drawElements(buffCxt, zsortedEles); + draw: function draw(context, centerX, centerY, width, height) { + this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); + }, - buffCxt.scale(1 / scale, 1 / scale); - buffCxt.translate(-translation.x, -translation.y); - } + intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { + return math.roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding); + }, - // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs - if (options.bg) { - buffCxt.globalCompositeOperation = 'destination-over'; + checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { - buffCxt.fillStyle = options.bg; - buffCxt.rect(0, 0, width, height); - buffCxt.fill(); - } - } + var cornerRadius = math.getRoundRectangleRadius(width, height); + var diam = cornerRadius * 2; - return buffCanvas; -}; + // Check hBox + if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) { + return true; + } -function b64ToBlob(b64, mimeType) { - var bytes = atob(b64); - var buff = new ArrayBuffer(bytes.length); - var buffUint8 = new Uint8Array(buff); + // Check vBox + if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) { + return true; + } - for (var i = 0; i < bytes.length; i++) { - buffUint8[i] = bytes.charCodeAt(i); - } + // Check top left quarter circle + if (math.checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) { - return new Blob([buff], { type: mimeType }); -} + return true; + } -function b64UriToB64(b64uri) { - var i = b64uri.indexOf(','); + // Check top right quarter circle + if (math.checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) { - return b64uri.substr(i + 1); -}; + return true; + } -function output(options, canvas, mimeType) { - var b64Uri = canvas.toDataURL(mimeType, options.quality); + // Check bottom right quarter circle + if (math.checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) { - switch (options.output) { - case 'blob': - return b64ToBlob(b64UriToB64(b64Uri), mimeType); + return true; + } - case 'base64': - return b64UriToB64(b64Uri); + // Check bottom left quarter circle + if (math.checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) { - case 'base64uri': - default: - return b64Uri; - } -} + return true; + } -CRp.png = function (options) { - return output(options, this.bufferCanvasImage(options), 'image/png'); + return false; + } + }; }; -CRp.jpg = function (options) { - return output(options, this.bufferCanvasImage(options), 'image/jpeg'); -}; +BRp.generateCutRectangle = function () { + return this.nodeShapes['cutrectangle'] = { + renderer: this, -module.exports = CRp; + name: 'cutrectangle', -/***/ }), -/* 111 */ -/***/ (function(module, exports, __webpack_require__) { + cornerLength: math.getCutRectangleCornerLength(), -"use strict"; + points: math.generateUnitNgonPointsFitToSquare(4, 0), + draw: function draw(context, centerX, centerY, width, height) { + this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); + }, -/* -The canvas renderer was written by Yue Dong. + generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) { + var cl = this.cornerLength; + var hh = height / 2; + var hw = width / 2; + var xBegin = centerX - hw; + var xEnd = centerX + hw; + var yBegin = centerY - hh; + var yEnd = centerY + hh; -Modifications tracked on Github. -*/ + // points are in clockwise order, inner (imaginary) triangle pt on [4, 5] + return { + topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl], + topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl], + bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl], + bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl] + }; + }, -var util = __webpack_require__(1); -var is = __webpack_require__(0); -var ElementTextureCache = __webpack_require__(109); -var LayeredTextureCache = __webpack_require__(112); + intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { + var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY); + var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]); -var CR = CanvasRenderer; -var CRp = CanvasRenderer.prototype; + return math.polygonIntersectLine(x, y, pts, nodeX, nodeY); + }, -CRp.CANVAS_LAYERS = 3; -// -CRp.SELECT_BOX = 0; -CRp.DRAG = 1; -CRp.NODE = 2; + checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { + // Check hBox + if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) { + return true; + } -CRp.BUFFER_COUNT = 3; -// -CRp.TEXTURE_BUFFER = 0; -CRp.MOTIONBLUR_BUFFER_NODE = 1; -CRp.MOTIONBLUR_BUFFER_DRAG = 2; + // Check vBox + if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) { + return true; + } + var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY); + return math.pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || math.pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || math.pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || math.pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft); + } -function CanvasRenderer(options) { - var r = this; + }; +}; - r.data = { - canvases: new Array(CRp.CANVAS_LAYERS), - contexts: new Array(CRp.CANVAS_LAYERS), - canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS), +BRp.generateBarrel = function () { + return this.nodeShapes['barrel'] = { + renderer: this, - bufferCanvases: new Array(CRp.BUFFER_COUNT), - bufferContexts: new Array(CRp.CANVAS_LAYERS) - }; + name: 'barrel', - var tapHlOff = '-webkit-tap-highlight-color: rgba(0,0,0,0);'; + points: math.generateUnitNgonPointsFitToSquare(4, 0), - r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef - var containerStyle = r.data.canvasContainer.style; - r.data.canvasContainer.setAttribute('style', tapHlOff); - containerStyle.position = 'relative'; - containerStyle.zIndex = '0'; - containerStyle.overflow = 'hidden'; + draw: function draw(context, centerX, centerY, width, height) { + this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); + }, - var container = options.cy.container(); - container.appendChild(r.data.canvasContainer); + intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { + var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY); - if ((container.getAttribute('style') || '').indexOf(tapHlOff) < 0) { - container.setAttribute('style', (container.getAttribute('style') || '') + tapHlOff); - } + var pts = [].concat(bPts.topLeft, bPts.topRight, bPts.bottomRight, bPts.bottomLeft); - for (var i = 0; i < CRp.CANVAS_LAYERS; i++) { - var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef - r.data.contexts[i] = canvas.getContext('2d'); - canvas.setAttribute('style', '-webkit-user-select: none; -moz-user-select: -moz-none; user-select: none; -webkit-tap-highlight-color: rgba(0,0,0,0); outline-style: none;' + (is.ms() ? ' -ms-touch-action: none; touch-action: none; ' : '')); - canvas.style.position = 'absolute'; - canvas.setAttribute('data-id', 'layer' + i); - canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i); - r.data.canvasContainer.appendChild(canvas); + return math.polygonIntersectLine(x, y, pts, nodeX, nodeY); + }, - r.data.canvasNeedsRedraw[i] = false; - } - r.data.topCanvas = r.data.canvases[0]; + generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) { + var hh = height / 2; + var hw = width / 2; + var xBegin = centerX - hw; + var xEnd = centerX + hw; + var yBegin = centerY - hh; + var yEnd = centerY + hh; - r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node'); - r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox'); - r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag'); + var curveConstants = math.getBarrelCurveConstants(width, height); + var hOffset = curveConstants.heightOffset; + var wOffset = curveConstants.widthOffset; + var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; - for (var i = 0; i < CRp.BUFFER_COUNT; i++) { - r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef - r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d'); - r.data.bufferCanvases[i].style.position = 'absolute'; - r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i); - r.data.bufferCanvases[i].style.zIndex = String(-i - 1); - r.data.bufferCanvases[i].style.visibility = 'hidden'; - //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]); - } + // points are in clockwise order, inner (imaginary) control pt on [4, 5] + var pts = { + topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin], + topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset], + bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd], + bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset] + }; - r.pathsEnabled = true; + pts.topLeft.isTop = true; + pts.topRight.isTop = true; + pts.bottomLeft.isBottom = true; + pts.bottomRight.isBottom = true; - r.data.eleTxrCache = new ElementTextureCache(r); - r.data.lyrTxrCache = new LayeredTextureCache(r, r.data.eleTxrCache); + return pts; + }, - r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) { - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var rs = ele._private.rstyle; - var de = rs.dirtyEvents; + checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { - if (ele.isNode() && de && de.length === 1 && de['position']) { - // then keep cached ele texture - } else { - r.data.eleTxrCache.invalidateElement(ele); + var curveConstants = math.getBarrelCurveConstants(width, height); + var hOffset = curveConstants.heightOffset; + var wOffset = curveConstants.widthOffset; + + // Check hBox + if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) { + return true; } - } - if (eles.length > 0) { - r.data.lyrTxrCache.invalidateElements(eles); - } - }); -} + // Check vBox + if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) { + return true; + } -CRp.redrawHint = function (group, bool) { - var r = this; + var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY); - switch (group) { - case 'eles': - r.data.canvasNeedsRedraw[CRp.NODE] = bool; - break; - case 'drag': - r.data.canvasNeedsRedraw[CRp.DRAG] = bool; - break; - case 'select': - r.data.canvasNeedsRedraw[CRp.SELECT_BOX] = bool; - break; - } -}; + var getCurveT = function getCurveT(x, y, curvePts) { + var x0 = curvePts[4]; + var x1 = curvePts[2]; + var x2 = curvePts[0]; + var y0 = curvePts[5]; + // var y1 = curvePts[ 3 ]; + var y2 = curvePts[1]; -// whether to use Path2D caching for drawing -var pathsImpld = typeof Path2D !== 'undefined'; + var xMin = Math.min(x0, x2); + var xMax = Math.max(x0, x2); + var yMin = Math.min(y0, y2); + var yMax = Math.max(y0, y2); -CRp.path2dEnabled = function (on) { - if (on === undefined) { - return this.pathsEnabled; - } + if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) { + var coeff = math.bezierPtsToQuadCoeff(x0, x1, x2); + var roots = math.solveQuadratic(coeff[0], coeff[1], coeff[2], x); - this.pathsEnabled = on ? true : false; -}; + var validRoots = roots.filter(function (r) { + return 0 <= r && r <= 1; + }); -CRp.usePaths = function () { - return pathsImpld && this.pathsEnabled; -}; + if (validRoots.length > 0) { + return validRoots[0]; + } + } + return null; + }; -[__webpack_require__(101), __webpack_require__(103), __webpack_require__(102), __webpack_require__(104), __webpack_require__(105), __webpack_require__(106), __webpack_require__(107), __webpack_require__(108), __webpack_require__(110), __webpack_require__(113)].forEach(function (props) { - util.extend(CRp, props); -}); + var curveRegions = Object.keys(barrelCurvePts); + for (var i = 0; i < curveRegions.length; i++) { + var corner = curveRegions[i]; + var cornerPts = barrelCurvePts[corner]; + var t = getCurveT(x, y, cornerPts); -module.exports = CR; + if (t == null) { + continue; + } -/***/ }), -/* 112 */ -/***/ (function(module, exports, __webpack_require__) { + var y0 = cornerPts[5]; + var y1 = cornerPts[3]; + var y2 = cornerPts[1]; + var bezY = math.qbezierAt(y0, y1, y2, t); -"use strict"; + if (cornerPts.isTop && bezY <= y) { + return true; + } + if (cornerPts.isBottom && y <= bezY) { + return true; + } + } + return false; + } + }; +}; +BRp.generateBottomRoundrectangle = function () { + return this.nodeShapes['bottomroundrectangle'] = { + renderer: this, -var util = __webpack_require__(1); -var math = __webpack_require__(2); -var Heap = __webpack_require__(8); -var is = __webpack_require__(0); -var defs = __webpack_require__(16); + name: 'bottomroundrectangle', -var defNumLayers = 1; // default number of layers to use -var minLvl = -4; // when scaling smaller than that we don't need to re-render -var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful) -var maxZoom = 3.99; // beyond this zoom level, layered textures are not used -var deqRedrawThreshold = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile -var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates -var disableEleImgSmoothing = true; // when drawing eles on layers from an ele cache ; crisper and more performant when true -var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame -var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time -var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing -var deqFastCost = 0.9; // % of frame time to be used when >60fps -var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch -var invalidThreshold = 250; // time threshold for disabling b/c of invalidations -var maxLayerArea = 4000 * 4000; // layers can't be bigger than this -var alwaysQueue = true; // never draw all the layers in a level on a frame; draw directly until all dequeued -var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm) + points: math.generateUnitNgonPointsFitToSquare(4, 0), -var useEleTxrCaching = true; // whether to use individual ele texture caching underneath this cache + draw: function draw(context, centerX, centerY, width, height) { + this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height); + }, -// var log = function(){ console.log.apply( console, arguments ); }; + intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) { + var topStartX = nodeX - (width / 2 + padding); + var topStartY = nodeY - (height / 2 + padding); + var topEndY = topStartY; + var topEndX = nodeX + (width / 2 + padding); -var LayeredTextureCache = function LayeredTextureCache(renderer, eleTxrCache) { - var self = this; + var topIntersections = math.finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false); + if (topIntersections.length > 0) { + return topIntersections; + } - var r = self.renderer = renderer; + return math.roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding); + }, - self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ] + checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) { - self.firstGet = true; + var cornerRadius = math.getRoundRectangleRadius(width, height); + var diam = 2 * cornerRadius; - self.lastInvalidationTime = util.performanceNow() - 2 * invalidThreshold; + // Check hBox + if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) { + return true; + } - self.skipping = false; + // Check vBox + if (math.pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) { + return true; + } - r.beforeRender(function (willDraw, now) { - if (now - self.lastInvalidationTime <= invalidThreshold) { - self.skipping = true; - } else { - self.skipping = false; - } - }); + // check non-rounded top side + var outerWidth = width / 2 + 2 * padding; + var outerHeight = height / 2 + 2 * padding; + var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight]; + if (math.pointInsidePolygonPoints(x, y, points)) { + return true; + } - var qSort = function qSort(a, b) { - return b.reqs - a.reqs; - }; + // Check bottom right quarter circle + if (math.checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) { - self.layersQueue = new Heap(qSort); + return true; + } - self.eleTxrCache = eleTxrCache; + // Check bottom left quarter circle + if (math.checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) { - self.setupEleCacheInvalidation(); + return true; + } - self.setupDequeueing(); + return false; + } + }; }; -var LTCp = LayeredTextureCache.prototype; +BRp.registerNodeShapes = function () { + var nodeShapes = this.nodeShapes = {}; + var renderer = this; -var layerIdPool = 0; -var MAX_INT = Math.pow(2, 53) - 1; + this.generateEllipse(); -LTCp.makeLayer = function (bb, lvl) { - var scale = Math.pow(2, lvl); + this.generatePolygon('triangle', math.generateUnitNgonPointsFitToSquare(3, 0)); - var w = Math.ceil(bb.w * scale); - var h = Math.ceil(bb.h * scale); + this.generatePolygon('rectangle', math.generateUnitNgonPointsFitToSquare(4, 0)); + nodeShapes['square'] = nodeShapes['rectangle']; - var canvas = document.createElement('canvas'); // eslint-disable-line no-undef + this.generateRoundRectangle(); - canvas.width = w; - canvas.height = h; + this.generateCutRectangle(); - var layer = { - id: layerIdPool = ++layerIdPool % MAX_INT, - bb: bb, - level: lvl, - width: w, - height: h, - canvas: canvas, - context: canvas.getContext('2d'), - eles: [], - elesQueue: [], - reqs: 0 - }; + this.generateBarrel(); - // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level); + this.generateBottomRoundrectangle(); - var cxt = layer.context; - var dx = -layer.bb.x1; - var dy = -layer.bb.y1; + this.generatePolygon('diamond', [0, 1, 1, 0, 0, -1, -1, 0]); - // do the transform on creation to save cycles (it's the same for all eles) - cxt.scale(scale, scale); - cxt.translate(dx, dy); + this.generatePolygon('pentagon', math.generateUnitNgonPointsFitToSquare(5, 0)); - return layer; -}; + this.generatePolygon('hexagon', math.generateUnitNgonPointsFitToSquare(6, 0)); + + this.generatePolygon('heptagon', math.generateUnitNgonPointsFitToSquare(7, 0)); + + this.generatePolygon('octagon', math.generateUnitNgonPointsFitToSquare(8, 0)); -LTCp.getLayers = function (eles, pxRatio, lvl) { - var self = this; - var r = self.renderer; - var cy = r.cy; - var zoom = cy.zoom(); - var firstGet = self.firstGet; + var star5Points = new Array(20); + { + var outerPoints = math.generateUnitNgonPoints(5, 0); + var innerPoints = math.generateUnitNgonPoints(5, Math.PI / 5); - self.firstGet = false; + // Outer radius is 1; inner radius of star is smaller + var innerRadius = 0.5 * (3 - Math.sqrt(5)); + innerRadius *= 1.57; - // log('--\nget layers with %s eles', eles.length); - //log eles.map(function(ele){ return ele.id() }) ); + for (var i = 0; i < innerPoints.length / 2; i++) { + innerPoints[i * 2] *= innerRadius; + innerPoints[i * 2 + 1] *= innerRadius; + } - if (lvl == null) { - lvl = Math.ceil(math.log2(zoom * pxRatio)); + for (var i = 0; i < 20 / 4; i++) { + star5Points[i * 4] = outerPoints[i * 2]; + star5Points[i * 4 + 1] = outerPoints[i * 2 + 1]; - if (lvl < minLvl) { - lvl = minLvl; - } else if (zoom >= maxZoom || lvl > maxLvl) { - return null; + star5Points[i * 4 + 2] = innerPoints[i * 2]; + star5Points[i * 4 + 3] = innerPoints[i * 2 + 1]; } } - self.validateLayersElesOrdering(lvl, eles); + star5Points = math.fitPolygonToSquare(star5Points); - var layersByLvl = self.layersByLevel; - var scale = Math.pow(2, lvl); - var layers = layersByLvl[lvl] = layersByLvl[lvl] || []; - var bb; + this.generatePolygon('star', star5Points); - var lvlComplete = self.levelIsComplete(lvl, eles); - var tmpLayers; + this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]); - var checkTempLevels = function checkTempLevels() { - var canUseAsTmpLvl = function canUseAsTmpLvl(l) { - self.validateLayersElesOrdering(l, eles); + this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]); - if (self.levelIsComplete(l, eles)) { - tmpLayers = layersByLvl[l]; - return true; - } - }; + this.generatePolygon('concavehexagon', [-1, -0.95, -0.75, 0, -1, 0.95, 1, 0.95, 0.75, 0, 1, -0.95]); - var checkLvls = function checkLvls(dir) { - if (tmpLayers) { - return; - } + this.generatePolygon('tag', [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1]); - for (var l = lvl + dir; minLvl <= l && l <= maxLvl; l += dir) { - if (canUseAsTmpLvl(l)) { - break; - } - } - }; + nodeShapes.makePolygon = function (points) { - checkLvls(+1); - checkLvls(-1); + // use caching on user-specified polygons so they are as fast as native shapes - // remove the invalid layers; they will be replaced as needed later in this function - for (var i = layers.length - 1; i >= 0; i--) { - var layer = layers[i]; + var key = points.join('$'); + var name = 'polygon-' + key; + var shape; - if (layer.invalid) { - util.removeFromArray(layers, layer); - } + if (shape = this[name]) { + // got cached shape + return shape; } + + // create and cache new shape + return renderer.generatePolygon(name, points); }; +}; - if (!lvlComplete) { - // if the current level is incomplete, then use the closest, best quality layerset temporarily - // and later queue the current layerset so we can get the proper quality level soon +module.exports = BRp; - checkTempLevels(); - } else { - // log('level complete, using existing layers\n--'); - return layers; - } +/***/ }), +/* 122 */ +/***/ (function(module, exports, __webpack_require__) { - var getBb = function getBb() { - if (!bb) { - bb = math.makeBoundingBox(); +"use strict"; - for (var i = 0; i < eles.length; i++) { - math.updateBoundingBox(bb, eles[i].boundingBox()); - } - } - return bb; - }; +var util = __webpack_require__(1); - var makeLayer = function makeLayer(opts) { - opts = opts || {}; +var BRp = {}; - var after = opts.after; +BRp.timeToRender = function () { + return this.redrawTotalTime / this.redrawCount; +}; - getBb(); +BRp.redraw = function (options) { + options = options || util.staticEmptyObject(); - var area = bb.w * scale * (bb.h * scale); + var r = this; - if (area > maxLayerArea) { - return null; - } + if (r.averageRedrawTime === undefined) { + r.averageRedrawTime = 0; + } + if (r.lastRedrawTime === undefined) { + r.lastRedrawTime = 0; + } + if (r.lastDrawTime === undefined) { + r.lastDrawTime = 0; + } - var layer = self.makeLayer(bb, lvl); + r.requestedFrame = true; + r.renderOptions = options; +}; - if (after != null) { - var index = layers.indexOf(after) + 1; +BRp.beforeRender = function (fn, priority) { + // the renderer can't add tick callbacks when destroyed + if (this.destroyed) { + return; + } - layers.splice(index, 0, layer); - } else if (opts.insert === undefined || opts.insert) { - // no after specified => first layer made so put at start - layers.unshift(layer); - } + priority = priority || 0; - // if( tmpLayers ){ - //self.queueLayer( layer ); - // } + var cbs = this.beforeRenderCallbacks; - return layer; - }; + cbs.push({ fn: fn, priority: priority }); - if (self.skipping && !firstGet) { - // log('skip layers'); - return null; + // higher priority callbacks executed first + cbs.sort(function (a, b) { + return b.priority - a.priority; + }); +}; + +var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) { + var cbs = r.beforeRenderCallbacks; + + for (var i = 0; i < cbs.length; i++) { + cbs[i].fn(willDraw, startTime); } +}; - // log('do layers'); +BRp.startRenderLoop = function () { + var r = this; - var layer = null; - var maxElesPerLayer = eles.length / defNumLayers; - var allowLazyQueueing = alwaysQueue && !firstGet; + if (r.renderLoopStarted) { + return; + } else { + r.renderLoopStarted = true; + } - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var rs = ele._private.rscratch; - var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; + var renderFn = function renderFn(requestTime) { + if (r.destroyed) { + return; + } - // log('look at ele', ele.id()); + if (r.requestedFrame && !r.skipFrame) { + beforeRenderCallbacks(r, true, requestTime); - var existingLayer = caches[lvl]; + var startTime = util.performanceNow(); - if (existingLayer) { - // reuse layer for later eles - // log('reuse layer for', ele.id()); - layer = existingLayer; - continue; - } + r.render(r.renderOptions); - if (!layer || layer.eles.length >= maxElesPerLayer || !math.boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) { - // log('make new layer for ele %s', ele.id()); + var endTime = r.lastDrawTime = util.performanceNow(); - layer = makeLayer({ insert: true, after: layer }); + if (r.averageRedrawTime === undefined) { + r.averageRedrawTime = endTime - startTime; + } - // if now layer can be built then we can't use layers at this level - if (!layer) { - return null; + if (r.redrawCount === undefined) { + r.redrawCount = 0; } - // log('new layer with id %s', layer.id); - } + r.redrawCount++; - if (tmpLayers || allowLazyQueueing) { - // log('queue ele %s in layer %s', ele.id(), layer.id); - self.queueLayer(layer, ele); - } else { - // log('draw ele %s in layer %s', ele.id(), layer.id); - self.drawEleInLayer(layer, ele, lvl, pxRatio); - } + if (r.redrawTotalTime === undefined) { + r.redrawTotalTime = 0; + } - layer.eles.push(ele); + var duration = endTime - startTime; - caches[lvl] = layer; - } + r.redrawTotalTime += duration; + r.lastRedrawTime = duration; - // log('--'); + // use a weighted average with a bias from the previous average so we don't spike so easily + r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2; - if (tmpLayers) { - // then we only queued the current layerset and can't draw it yet - return tmpLayers; - } + r.requestedFrame = false; + } else { + beforeRenderCallbacks(r, false, requestTime); + } - if (allowLazyQueueing) { - // log('lazy queue level', lvl); - return null; - } + r.skipFrame = false; - return layers; -}; + util.requestAnimationFrame(renderFn); + }; -// a layer may want to use an ele cache of a higher level to avoid blurriness -// so the layer level might not equal the ele level -LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) { - return lvl; + util.requestAnimationFrame(renderFn); }; -function imgSmoothing(context, bool) { - if (context.imageSmoothingEnabled != null) { - context.imageSmoothingEnabled = bool; - } else { - context.webkitImageSmoothingEnabled = bool; - context.mozImageSmoothingEnabled = bool; - context.msImageSmoothingEnabled = bool; - } -} +module.exports = BRp; -LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) { - var self = this; - var r = this.renderer; - var context = layer.context; - var bb = ele.boundingBox(); +/***/ }), +/* 123 */ +/***/ (function(module, exports, __webpack_require__) { - if (bb.w === 0 || bb.h === 0 || !ele.visible()) { - return; - } +"use strict"; - var eleCache = self.eleTxrCache; - var reason = useHighQualityEleTxrReqs ? eleCache.reasons.highQuality : undefined; - lvl = self.getEleLevelForLayerLevel(lvl, pxRatio); +/* +The canvas renderer was written by Yue Dong. - var cache = useEleTxrCaching ? eleCache.getElement(ele, bb, null, lvl, reason) : null; +Modifications tracked on Github. +*/ - if (cache) { - if (disableEleImgSmoothing) { - imgSmoothing(context, false); - } +var util = __webpack_require__(1); +var is = __webpack_require__(0); +var ElementTextureCache = __webpack_require__(124); +var LayeredTextureCache = __webpack_require__(125); - context.drawImage(cache.texture.canvas, cache.x, 0, cache.width, cache.height, bb.x1, bb.y1, bb.w, bb.h); +var CR = CanvasRenderer; +var CRp = CanvasRenderer.prototype; - if (disableEleImgSmoothing) { - imgSmoothing(context, true); - } - } else { - // if the element is not cacheable, then draw directly - r.drawElement(context, ele); - } -}; +CRp.CANVAS_LAYERS = 3; +// +CRp.SELECT_BOX = 0; +CRp.DRAG = 1; +CRp.NODE = 2; -LTCp.levelIsComplete = function (lvl, eles) { - var self = this; - var layers = self.layersByLevel[lvl]; +CRp.BUFFER_COUNT = 3; +// +CRp.TEXTURE_BUFFER = 0; +CRp.MOTIONBLUR_BUFFER_NODE = 1; +CRp.MOTIONBLUR_BUFFER_DRAG = 2; - if (!layers || layers.length === 0) { - return false; - } +function CanvasRenderer(options) { + var r = this; - var numElesInLayers = 0; + r.data = { + canvases: new Array(CRp.CANVAS_LAYERS), + contexts: new Array(CRp.CANVAS_LAYERS), + canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS), - for (var i = 0; i < layers.length; i++) { - var layer = layers[i]; + bufferCanvases: new Array(CRp.BUFFER_COUNT), + bufferContexts: new Array(CRp.CANVAS_LAYERS) + }; - // if there are any eles needed to be drawn yet, the level is not complete - if (layer.reqs > 0) { - return false; - } + var tapHlOff = '-webkit-tap-highlight-color: rgba(0,0,0,0);'; - // if the layer is invalid, the level is not complete - if (layer.invalid) { - return false; - } + r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef + var containerStyle = r.data.canvasContainer.style; + r.data.canvasContainer.setAttribute('style', tapHlOff); + containerStyle.position = 'relative'; + containerStyle.zIndex = '0'; + containerStyle.overflow = 'hidden'; - numElesInLayers += layer.eles.length; - } + var container = options.cy.container(); + container.appendChild(r.data.canvasContainer); - // we should have exactly the number of eles passed in to be complete - if (numElesInLayers !== eles.length) { - return false; + if ((container.getAttribute('style') || '').indexOf(tapHlOff) < 0) { + container.setAttribute('style', (container.getAttribute('style') || '') + tapHlOff); } - return true; -}; + for (var i = 0; i < CRp.CANVAS_LAYERS; i++) { + var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef + r.data.contexts[i] = canvas.getContext('2d'); + canvas.setAttribute('style', '-webkit-user-select: none; -moz-user-select: -moz-none; user-select: none; -webkit-tap-highlight-color: rgba(0,0,0,0); outline-style: none;' + (is.ms() ? ' -ms-touch-action: none; touch-action: none; ' : '')); + canvas.style.position = 'absolute'; + canvas.setAttribute('data-id', 'layer' + i); + canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i); + r.data.canvasContainer.appendChild(canvas); -LTCp.validateLayersElesOrdering = function (lvl, eles) { - var layers = this.layersByLevel[lvl]; + r.data.canvasNeedsRedraw[i] = false; + } + r.data.topCanvas = r.data.canvases[0]; - if (!layers) { - return; + r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node'); + r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox'); + r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag'); + + for (var i = 0; i < CRp.BUFFER_COUNT; i++) { + r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef + r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d'); + r.data.bufferCanvases[i].style.position = 'absolute'; + r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i); + r.data.bufferCanvases[i].style.zIndex = String(-i - 1); + r.data.bufferCanvases[i].style.visibility = 'hidden'; + //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]); } - // if in a layer the eles are not in the same order, then the layer is invalid - // (i.e. there is an ele in between the eles in the layer) + r.pathsEnabled = true; - for (var i = 0; i < layers.length; i++) { - var layer = layers[i]; - var offset = -1; + r.data.eleTxrCache = new ElementTextureCache(r); + r.data.lyrTxrCache = new LayeredTextureCache(r, r.data.eleTxrCache); - // find the offset - for (var j = 0; j < eles.length; j++) { - if (layer.eles[0] === eles[j]) { - offset = j; - break; + r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) { + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var rs = ele._private.rstyle; + var de = rs.dirtyEvents; + + if (ele.isNode() && de && de.length === 1 && de['position']) { + // then keep cached ele texture + } else { + r.data.eleTxrCache.invalidateElement(ele); } } - if (offset < 0) { - // then the layer has nonexistant elements and is invalid - this.invalidateLayer(layer); - continue; + if (eles.length > 0) { + r.data.lyrTxrCache.invalidateElements(eles); } + }); +} - // the eles in the layer must be in the same continuous order, else the layer is invalid +CRp.redrawHint = function (group, bool) { + var r = this; - var o = offset; + switch (group) { + case 'eles': + r.data.canvasNeedsRedraw[CRp.NODE] = bool; + break; + case 'drag': + r.data.canvasNeedsRedraw[CRp.DRAG] = bool; + break; + case 'select': + r.data.canvasNeedsRedraw[CRp.SELECT_BOX] = bool; + break; + } +}; - for (var j = 0; j < layer.eles.length; j++) { - if (layer.eles[j] !== eles[o + j]) { - // log('invalidate based on ordering', layer.id); +// whether to use Path2D caching for drawing +var pathsImpld = typeof Path2D !== 'undefined'; - this.invalidateLayer(layer); - break; - } - } +CRp.path2dEnabled = function (on) { + if (on === undefined) { + return this.pathsEnabled; } -}; -LTCp.updateElementsInLayers = function (eles, update) { - var self = this; - var isEles = is.element(eles[0]); + this.pathsEnabled = on ? true : false; +}; - // collect udpated elements (cascaded from the layers) and update each - // layer itself along the way - for (var i = 0; i < eles.length; i++) { - var req = isEles ? null : eles[i]; - var ele = isEles ? eles[i] : eles[i].ele; - var rs = ele._private.rscratch; - var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; +CRp.usePaths = function () { + return pathsImpld && this.pathsEnabled; +}; - for (var l = minLvl; l <= maxLvl; l++) { - var layer = caches[l]; +[__webpack_require__(126), __webpack_require__(127), __webpack_require__(128), __webpack_require__(129), __webpack_require__(130), __webpack_require__(131), __webpack_require__(132), __webpack_require__(133), __webpack_require__(134), __webpack_require__(135)].forEach(function (props) { + util.extend(CRp, props); +}); - if (!layer) { - continue; - } +module.exports = CR; - // if update is a request from the ele cache, then it affects only - // the matching level - if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) { - continue; - } +/***/ }), +/* 124 */ +/***/ (function(module, exports, __webpack_require__) { - update(layer, ele, req); - } - } -}; +"use strict"; -LTCp.haveLayers = function () { - var self = this; - var haveLayers = false; - for (var l = minLvl; l <= maxLvl; l++) { - var layers = self.layersByLevel[l]; +var math = __webpack_require__(2); +var util = __webpack_require__(1); +var Heap = __webpack_require__(9); +var defs = __webpack_require__(19); - if (layers && layers.length > 0) { - haveLayers = true; - break; - } - } +var minTxrH = 25; // the size of the texture cache for small height eles (special case) +var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up +var minLvl = -4; // when scaling smaller than that we don't need to re-render +var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful) +var maxZoom = 3.99; // beyond this zoom level, layered textures are not used +var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps +var defTxrWidth = 1024; // default/minimum texture width +var maxTxrW = 1024; // the maximum width of a texture +var maxTxrH = 1024; // the maximum height of a texture +var minUtility = 0.5; // if usage of texture is less than this, it is retired +var maxFullness = 0.8; // fullness of texture after which queue removal is checked +var maxFullnessChecks = 10; // dequeued after this many checks +var allowEdgeTxrCaching = false; // whether edges can be cached as textures (TODO maybe better on if webgl supported?) +var allowParentTxrCaching = false; // whether parent nodes can be cached as textures (TODO maybe better on if webgl supported?) +var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame +var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time +var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing +var deqFastCost = 0.9; // % of frame time to be used when >60fps +var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile +var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch - return haveLayers; +var getTxrReasons = { + dequeue: 'dequeue', + downscale: 'downscale', + highQuality: 'highQuality' }; -LTCp.invalidateElements = function (eles) { +var ElementTextureCache = function ElementTextureCache(renderer) { var self = this; - self.lastInvalidationTime = util.performanceNow(); - - // log('update invalidate layer time from eles'); - - if (eles.length === 0 || !self.haveLayers()) { - return; - } + self.renderer = renderer; + self.onDequeues = []; - self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) { - self.invalidateLayer(layer); - }); + self.setupDequeueing(); }; -LTCp.invalidateLayer = function (layer) { - // log('update invalidate layer time'); - - this.lastInvalidationTime = util.performanceNow(); - - if (layer.invalid) { - return; - } // save cycles - - var lvl = layer.level; - var eles = layer.eles; - var layers = this.layersByLevel[lvl]; - - // log('invalidate layer', layer.id ); +var ETCp = ElementTextureCache.prototype; - util.removeFromArray(layers, layer); - // layer.eles = []; +ETCp.reasons = getTxrReasons; - layer.elesQueue = []; +// the list of textures in which new subtextures for elements can be placed +ETCp.getTextureQueue = function (txrH) { + var self = this; + self.eleImgCaches = self.eleImgCaches || {}; - layer.invalid = true; + return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || []; +}; - if (layer.replacement) { - layer.replacement.invalid = true; - } +// the list of usused textures which can be recycled (in use in texture queue) +ETCp.getRetiredTextureQueue = function (txrH) { + var self = this; - for (var i = 0; i < eles.length; i++) { - var caches = eles[i]._private.rscratch.imgLayerCaches; + var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {}; + var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || []; - if (caches) { - caches[lvl] = null; - } - } + return rtxtrQ; }; -LTCp.refineElementTextures = function (eles) { +// queue of element draw requests at different scale levels +ETCp.getElementQueue = function () { var self = this; - // log('refine', eles.length); - - self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) { - var rLyr = layer.replacement; + var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) { + return b.reqs - a.reqs; + }); - if (!rLyr) { - rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level); - rLyr.replaces = layer; - rLyr.eles = layer.eles; + return q; +}; - // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level); - } +// queue of element draw requests at different scale levels (element id lookup) +ETCp.getElementIdToQueue = function () { + var self = this; - if (!rLyr.reqs) { - for (var i = 0; i < rLyr.eles.length; i++) { - self.queueLayer(rLyr, rLyr.eles[i]); - } + var id2q = self.eleIdToCacheQueue = self.eleIdToCacheQueue || {}; - // log('queue replacement layer refinement', rLyr.id); - } - }); + return id2q; }; -LTCp.setupEleCacheInvalidation = function () { +ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) { var self = this; - var eleDeqs = []; + var r = this.renderer; + var rs = ele._private.rscratch; + var zoom = r.cy.zoom(); - if (!useEleTxrCaching) { - return; + if (bb.w === 0 || bb.h === 0 || !ele.visible()) { + return null; } - var updatedElesInLayers = util.debounce(function () { - self.refineElementTextures(eleDeqs); + if (lvl == null) { + lvl = Math.ceil(math.log2(zoom * pxRatio)); + } - eleDeqs = []; - }, refineEleDebounceTime); + if (lvl < minLvl) { + lvl = minLvl; + } else if (zoom >= maxZoom || lvl > maxLvl) { + return null; + } - self.eleTxrCache.onDequeue(function (reqs) { - for (var i = 0; i < reqs.length; i++) { - eleDeqs.push(reqs[i]); - } + var scale = Math.pow(2, lvl); + var eleScaledH = bb.h * scale; + var eleScaledW = bb.w * scale; + var caches = rs.imgCaches = rs.imgCaches || {}; + var eleCache = caches[lvl]; - updatedElesInLayers(); - }); -}; + if (eleCache) { + return eleCache; + } -LTCp.queueLayer = function (layer, ele) { - var self = this; - var q = self.layersQueue; - var elesQ = layer.elesQueue; - var hasId = elesQ.hasId = elesQ.hasId || {}; + var txrH; // which texture height this ele belongs to - // if a layer is going to be replaced, queuing is a waste of time - if (layer.replacement) { - return; + if (eleScaledH <= minTxrH) { + txrH = minTxrH; + } else if (eleScaledH <= txrStepH) { + txrH = txrStepH; + } else { + txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH; } - if (ele) { - if (hasId[ele.id()]) { - return; - } - - elesQ.push(ele); - hasId[ele.id()] = true; + if (eleScaledH > maxTxrH || eleScaledW > maxTxrW || !allowEdgeTxrCaching && ele.isEdge() || !allowParentTxrCaching && ele.isParent()) { + return null; // caching large elements is not efficient } - if (layer.reqs) { - layer.reqs++; + var txrQ = self.getTextureQueue(txrH); - q.updateItem(layer); - } else { - layer.reqs = 1; + // first try the second last one in case it has space at the end + var txr = txrQ[txrQ.length - 2]; - q.push(layer); + var addNewTxr = function addNewTxr() { + return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW); + }; + + // try the last one if there is no second last one + if (!txr) { + txr = txrQ[txrQ.length - 1]; } -}; -LTCp.dequeue = function (pxRatio) { - var self = this; - var q = self.layersQueue; - var deqd = []; - var eleDeqs = 0; + // if the last one doesn't exist, we need a first one + if (!txr) { + txr = addNewTxr(); + } - while (eleDeqs < maxDeqSize) { - if (q.size() === 0) { - break; - } + // if there's no room in the current texture, we need a new one + if (txr.width - txr.usedWidth < eleScaledW) { + txr = addNewTxr(); + } - var layer = q.peek(); + var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale); + var scalableFrom = function scalableFrom(otherCache) { + return otherCache && otherCache.scaledLabelShown === scaledLabelShown; + }; - // if a layer has been or will be replaced, then don't waste time with it - if (layer.replacement) { - // log('layer %s in queue skipped b/c it already has a replacement', layer.id); - q.pop(); - continue; - } + var deqing = reason && reason === getTxrReasons.dequeue; + var highQualityReq = reason && reason === getTxrReasons.highQuality; + var downscaleReq = reason && reason === getTxrReasons.downscale; - // if this is a replacement layer that has been superceded, then forget it - if (layer.replaces && layer !== layer.replaces.replacement) { - // log('layer is no longer the most uptodate replacement; dequeued', layer.id) - q.pop(); - continue; - } + var higherCache; // the nearest cache with a higher level + for (var l = lvl + 1; l <= maxLvl; l++) { + var c = caches[l]; - if (layer.invalid) { - // log('replacement layer %s is invalid; dequeued', layer.id); - q.pop(); - continue; + if (c) { + higherCache = c;break; } + } - var ele = layer.elesQueue.shift(); + var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null; - if (ele) { - // log('dequeue layer %s', layer.id); + var downscale = function downscale() { + txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH); + }; - self.drawEleInLayer(layer, ele, layer.level, pxRatio); + // reset ele area in texture + txr.context.setTransform(1, 0, 0, 1, 0, 0); + txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH); - eleDeqs++; - } + if (scalableFrom(oneUpCache)) { + // then we can relatively cheaply rescale the existing image w/o rerendering + downscale(); + } else if (scalableFrom(higherCache)) { + // then use the higher cache for now and queue the next level down + // to cheaply scale towards the smaller level - if (deqd.length === 0) { - // we need only one entry in deqd to queue redrawing etc - deqd.push(true); + if (highQualityReq) { + for (var l = higherCache.level; l > lvl; l--) { + oneUpCache = self.getElement(ele, bb, pxRatio, l, getTxrReasons.downscale); + } + + downscale(); + } else { + self.queueElement(ele, higherCache.level - 1); + + return higherCache; } + } else { - // if the layer has all its eles done, then remove from the queue - if (layer.elesQueue.length === 0) { - q.pop(); + var lowerCache; // the nearest cache with a lower level + if (!deqing && !highQualityReq && !downscaleReq) { + for (var l = lvl - 1; l >= minLvl; l--) { + var c = caches[l]; - layer.reqs = 0; + if (c) { + lowerCache = c;break; + } + } + } - // log('dequeue of layer %s complete', layer.id); + if (scalableFrom(lowerCache)) { + // then use the lower quality cache for now and queue the better one for later - // when a replacement layer is dequeued, it replaces the old layer in the level - if (layer.replaces) { - self.applyLayerReplacement(layer); - } + self.queueElement(ele, lvl); - self.requestRedraw(); + return lowerCache; } - } - return deqd; -}; + txr.context.translate(txr.usedWidth, 0); + txr.context.scale(scale, scale); -LTCp.applyLayerReplacement = function (layer) { - var self = this; - var layersInLevel = self.layersByLevel[layer.level]; - var replaced = layer.replaces; - var index = layersInLevel.indexOf(replaced); + r.drawElement(txr.context, ele, bb, scaledLabelShown); - // if the replaced layer is not in the active list for the level, then replacing - // refs would be a mistake (i.e. overwriting the true active layer) - if (index < 0 || replaced.invalid) { - // log('replacement layer would have no effect', layer.id); - return; + txr.context.scale(1 / scale, 1 / scale); + txr.context.translate(-txr.usedWidth, 0); } - layersInLevel[index] = layer; // replace level ref + eleCache = caches[lvl] = { + ele: ele, + x: txr.usedWidth, + texture: txr, + level: lvl, + scale: scale, + width: eleScaledW, + height: eleScaledH, + scaledLabelShown: scaledLabelShown + }; - // replace refs in eles - for (var i = 0; i < layer.eles.length; i++) { - var _p = layer.eles[i]._private; - var cache = _p.imgLayerCaches = _p.imgLayerCaches || {}; + txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing); - if (cache) { - cache[layer.level] = layer; - } - } + txr.eleCaches.push(eleCache); - // log('apply replacement layer %s over %s', layer.id, replaced.id); + self.checkTextureFullness(txr); - self.requestRedraw(); + return eleCache; }; -LTCp.requestRedraw = util.debounce(function () { - var r = this.renderer; +ETCp.invalidateElement = function (ele) { + var self = this; + var caches = ele._private.rscratch.imgCaches; - r.redrawHint('eles', true); - r.redrawHint('drag', true); - r.redraw(); -}, 100); + if (caches) { + for (var lvl = minLvl; lvl <= maxLvl; lvl++) { + var cache = caches[lvl]; -LTCp.setupDequeueing = defs.setupDequeueing({ - deqRedrawThreshold: deqRedrawThreshold, - deqCost: deqCost, - deqAvgCost: deqAvgCost, - deqNoDrawCost: deqNoDrawCost, - deqFastCost: deqFastCost, - deq: function deq(self, pxRatio) { - return self.dequeue(pxRatio); - }, - onDeqd: util.noop, - shouldRedraw: util.trueify, - priority: function priority(self) { - return self.renderer.beforeRenderPriorities.lyrTxrDeq; - } -}); + if (cache) { + var txr = cache.texture; -module.exports = LayeredTextureCache; + // remove space from the texture it belongs to + txr.invalidatedWidth += cache.width; -/***/ }), -/* 113 */ -/***/ (function(module, exports, __webpack_require__) { + // remove refs with the element + caches[lvl] = null; + util.removeFromArray(txr.eleCaches, cache); -"use strict"; + // remove from queue since the old req was for the old state + self.removeFromQueue(ele); + // might have to remove the entire texture if it's not efficiently using its space + self.checkTextureUtility(txr); + } + } + } +}; -var CRp = {}; +ETCp.checkTextureUtility = function (txr) { + // invalidate all entries in the cache if the cache size is small + if (txr.invalidatedWidth >= minUtility * txr.width) { + this.retireTexture(txr); + } +}; -CRp.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) { - switch (name) { - case 'ellipse': - return this.drawEllipsePath(context, centerX, centerY, width, height); - case 'polygon': - return this.drawPolygonPath(context, centerX, centerY, width, height, points); - case 'roundrectangle': - return this.drawRoundRectanglePath(context, centerX, centerY, width, height); - case 'cutrectangle': - return this.drawCutRectanglePath(context, centerX, centerY, width, height); - case 'bottomroundrectangle': - return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height); - case 'barrel': - return this.drawBarrelPath(context, centerX, centerY, width, height); +ETCp.checkTextureFullness = function (txr) { + // if texture has been mostly filled and passed over several times, remove + // it from the queue so we don't need to waste time looking at it to put new things + + var self = this; + var txrQ = self.getTextureQueue(txr.height); + + if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) { + util.removeFromArray(txrQ, txr); + } else { + txr.fullnessChecks++; } }; -module.exports = CRp; +ETCp.retireTexture = function (txr) { + var self = this; + var txrH = txr.height; + var txrQ = self.getTextureQueue(txrH); -/***/ }), -/* 114 */ -/***/ (function(module, exports, __webpack_require__) { + // retire the texture from the active / searchable queue: -"use strict"; + util.removeFromArray(txrQ, txr); + txr.retired = true; -module.exports = [{ name: 'null', impl: __webpack_require__(115) }, { name: 'base', impl: __webpack_require__(97) }, { name: 'canvas', impl: __webpack_require__(111) }]; + // remove the refs from the eles to the caches: -/***/ }), -/* 115 */ -/***/ (function(module, exports, __webpack_require__) { + var eleCaches = txr.eleCaches; -"use strict"; + for (var i = 0; i < eleCaches.length; i++) { + var eleCache = eleCaches[i]; + var ele = eleCache.ele; + var lvl = eleCache.level; + var imgCaches = ele._private.rscratch.imgCaches; + if (imgCaches) { + imgCaches[lvl] = null; + } + } -function NullRenderer(options) { - this.options = options; - this.notifications = 0; // for testing -} + util.clearArray(eleCaches); -var noop = function noop() {}; + // add the texture to a retired queue so it can be recycled in future: -NullRenderer.prototype = { - recalculateRenderedStyle: noop, - notify: function notify() { - this.notifications++; - }, - init: noop -}; + var rtxtrQ = self.getRetiredTextureQueue(txrH); -module.exports = NullRenderer; + rtxtrQ.push(txr); +}; -/***/ }), -/* 116 */ -/***/ (function(module, exports, __webpack_require__) { +ETCp.addTexture = function (txrH, minW) { + var self = this; + var txrQ = self.getTextureQueue(txrH); + var txr = {}; -"use strict"; + txrQ.push(txr); + txr.eleCaches = []; -var is = __webpack_require__(0); -var Core = __webpack_require__(12); -var extension = __webpack_require__(20); -var Stylesheet = __webpack_require__(21); + txr.height = txrH; + txr.width = Math.max(defTxrWidth, minW); + txr.usedWidth = 0; + txr.invalidatedWidth = 0; + txr.fullnessChecks = 0; -var cytoscape = function cytoscape(options) { - // jshint ignore:line - // if no options specified, use default - if (options === undefined) { - options = {}; - } + txr.canvas = document.createElement('canvas'); // eslint-disable-line no-undef + txr.canvas.width = txr.width; + txr.canvas.height = txr.height; - // create instance - if (is.plainObject(options)) { - return new Core(options); - } + txr.context = txr.canvas.getContext('2d'); - // allow for registration of extensions - else if (is.string(options)) { - return extension.apply(extension, arguments); - } + return txr; }; -// e.g. cytoscape.use( require('cytoscape-foo'), bar ) -cytoscape.use = function (ext) { - var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext +ETCp.recycleTexture = function (txrH, minW) { + var self = this; + var txrQ = self.getTextureQueue(txrH); + var rtxtrQ = self.getRetiredTextureQueue(txrH); - args.unshift(cytoscape); // cytoscape is first arg to ext + for (var i = 0; i < rtxtrQ.length; i++) { + var txr = rtxtrQ[i]; - ext.apply(null, args); + if (txr.width >= minW) { + txr.retired = false; - return this; -}; + txr.usedWidth = 0; + txr.invalidatedWidth = 0; + txr.fullnessChecks = 0; -// replaced by build system -cytoscape.version = __webpack_require__(22); + util.clearArray(txr.eleCaches); -// expose public apis (mostly for extensions) -cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet; + txr.context.setTransform(1, 0, 0, 1, 0, 0); + txr.context.clearRect(0, 0, txr.width, txr.height); -module.exports = cytoscape; + util.removeFromArray(rtxtrQ, txr); + txrQ.push(txr); -/***/ }), -/* 117 */ -/***/ (function(module, exports, __webpack_require__) { + return txr; + } + } +}; -"use strict"; +ETCp.queueElement = function (ele, lvl) { + var self = this; + var q = self.getElementQueue(); + var id2q = self.getElementIdToQueue(); + var id = ele.id(); + var existingReq = id2q[id]; + if (existingReq) { + // use the max lvl b/c in between lvls are cheap to make + existingReq.level = Math.max(existingReq.level, lvl); + existingReq.reqs++; -function ObjectMap() { - this._obj = {}; -} + q.updateItem(existingReq); + } else { + var req = { + ele: ele, + level: lvl, + reqs: 1 + }; -var p = ObjectMap.prototype; + q.push(req); -p.set = function (key, val) { - this._obj[key] = val; + id2q[id] = req; + } }; -p.delete = function (key) { - this._obj[key] = null; -}; +ETCp.dequeue = function (pxRatio /*, extent*/) { + var self = this; + var q = self.getElementQueue(); + var id2q = self.getElementIdToQueue(); + var dequeued = []; -p.has = function (key) { - return this._obj[key] != null; -}; + for (var i = 0; i < maxDeqSize; i++) { + if (q.size() > 0) { + var req = q.pop(); + var ele = req.ele; + var caches = ele._private.rscratch.imgCaches; -p.get = function (key) { - return this._obj[key]; -}; + // dequeueing isn't necessary when an existing cache exists + if (caches[req.level] != null) { + continue; + } -// TODO use the stdlib Map in future... -// module.exports = typeof Map !== 'undefined' ? Map : ObjectMap; -module.exports = ObjectMap; + id2q[ele.id()] = null; -/***/ }), -/* 118 */ -/***/ (function(module, exports, __webpack_require__) { + dequeued.push(req); -"use strict"; + var bb = ele.boundingBox(); + self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue); + } else { + break; + } + } -var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); + return dequeued; +}; -var _require = __webpack_require__(17), - stateSelectorRegex = _require.stateSelectorRegex; +ETCp.removeFromQueue = function (ele) { + var self = this; + var q = self.getElementQueue(); + var id2q = self.getElementIdToQueue(); + var req = id2q[ele.id()]; -var tokens = __webpack_require__(121); -var util = __webpack_require__(1); -var newQuery = __webpack_require__(11); + if (req != null) { + // bring to front of queue + req.reqs = util.MAX_INT; + q.updateItem(req); -// when a token like a variable has escaped meta characters, we need to clean the backslashes out -// so that values get compared properly in Selector.filter() -var cleanMetaChars = function cleanMetaChars(str) { - return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) { - return $1; - }); -}; + q.pop(); // remove from queue -var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) { - if (examiningQuery === selector[selector.length - 1]) { - selector[selector.length - 1] = replacementQuery; + id2q[ele.id()] = null; // remove from lookup map } }; -// NOTE: add new expression syntax here to have it recognised by the parser; -// - a query contains all adjacent (i.e. no separator in between) expressions; -// - the current query is stored in selector[i] --- you can use the reference to `this` in the populate function; -// - you need to check the query objects in Selector.filter() for it actually filter properly, but that's pretty straight forward -// - when you add something here, also add to Selector.toString() -var exprs = [{ - name: 'group', - query: true, - regex: '(' + tokens.group + ')', - populate: function populate(selector, query, _ref) { - var _ref2 = _slicedToArray(_ref, 1), - group = _ref2[0]; +ETCp.onDequeue = function (fn) { + this.onDequeues.push(fn); +}; +ETCp.offDequeue = function (fn) { + util.removeFromArray(this.onDequeues, fn); +}; - query.group = group === '*' ? group : group + 's'; - } -}, { - name: 'state', - query: true, - regex: stateSelectorRegex, - populate: function populate(selector, query, _ref3) { - var _ref4 = _slicedToArray(_ref3, 1), - state = _ref4[0]; +ETCp.setupDequeueing = defs.setupDequeueing({ + deqRedrawThreshold: deqRedrawThreshold, + deqCost: deqCost, + deqAvgCost: deqAvgCost, + deqNoDrawCost: deqNoDrawCost, + deqFastCost: deqFastCost, + deq: function deq(self, pxRatio, extent) { + return self.dequeue(pxRatio, extent); + }, + onDeqd: function onDeqd(self, deqd) { + for (var i = 0; i < self.onDequeues.length; i++) { + var fn = self.onDequeues[i]; - query.colonSelectors.push(state); - } -}, { - name: 'id', - query: true, - regex: '\\#(' + tokens.id + ')', - populate: function populate(selector, query, _ref5) { - var _ref6 = _slicedToArray(_ref5, 1), - id = _ref6[0]; + fn(deqd); + } + }, + shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) { + for (var i = 0; i < deqd.length; i++) { + var bb = deqd[i].ele.boundingBox(); - query.ids.push(cleanMetaChars(id)); - } -}, { - name: 'className', - query: true, - regex: '\\.(' + tokens.className + ')', - populate: function populate(selector, query, _ref7) { - var _ref8 = _slicedToArray(_ref7, 1), - className = _ref8[0]; + if (math.boundingBoxesIntersect(bb, extent)) { + return true; + } + } - query.classes.push(cleanMetaChars(className)); + return false; + }, + priority: function priority(self) { + return self.renderer.beforeRenderPriorities.eleTxrDeq; } -}, { - name: 'dataExists', - query: true, - regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]', - populate: function populate(selector, query, _ref9) { - var _ref10 = _slicedToArray(_ref9, 1), - variable = _ref10[0]; +}); - query.data.push({ - field: cleanMetaChars(variable) - }); - } -}, { - name: 'dataCompare', - query: true, - regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]', - populate: function populate(selector, query, _ref11) { - var _ref12 = _slicedToArray(_ref11, 3), - variable = _ref12[0], - comparatorOp = _ref12[1], - value = _ref12[2]; +module.exports = ElementTextureCache; - var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null; +/***/ }), +/* 125 */ +/***/ (function(module, exports, __webpack_require__) { - if (valueIsString) { - value = value.substring(1, value.length - 1); - } else { - value = parseFloat(value); - } +"use strict"; - query.data.push({ - field: cleanMetaChars(variable), - operator: comparatorOp, - value: value - }); - } -}, { - name: 'dataBool', - query: true, - regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]', - populate: function populate(selector, query, _ref13) { - var _ref14 = _slicedToArray(_ref13, 2), - boolOp = _ref14[0], - variable = _ref14[1]; - query.data.push({ - field: cleanMetaChars(variable), - operator: boolOp - }); - } -}, { - name: 'metaCompare', - query: true, - regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]', - populate: function populate(selector, query, _ref15) { - var _ref16 = _slicedToArray(_ref15, 3), - meta = _ref16[0], - comparatorOp = _ref16[1], - number = _ref16[2]; +var util = __webpack_require__(1); +var math = __webpack_require__(2); +var Heap = __webpack_require__(9); +var is = __webpack_require__(0); +var defs = __webpack_require__(19); - query.meta.push({ - field: cleanMetaChars(meta), - operator: comparatorOp, - value: parseFloat(number) - }); - } -}, { - name: 'nextQuery', - separator: true, - regex: tokens.separator, - populate: function populate(selector) { - // go on to next query - var nextQuery = selector[selector.length++] = newQuery(); - selector.currentSubject = null; +var defNumLayers = 1; // default number of layers to use +var minLvl = -4; // when scaling smaller than that we don't need to re-render +var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful) +var maxZoom = 3.99; // beyond this zoom level, layered textures are not used +var deqRedrawThreshold = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile +var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates +var disableEleImgSmoothing = true; // when drawing eles on layers from an ele cache ; crisper and more performant when true +var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame +var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time +var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing +var deqFastCost = 0.9; // % of frame time to be used when >60fps +var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch +var invalidThreshold = 250; // time threshold for disabling b/c of invalidations +var maxLayerArea = 4000 * 4000; // layers can't be bigger than this +var alwaysQueue = true; // never draw all the layers in a level on a frame; draw directly until all dequeued +var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm) + +var useEleTxrCaching = true; // whether to use individual ele texture caching underneath this cache + +// var log = function(){ console.log.apply( console, arguments ); }; - return nextQuery; - } -}, { - name: 'directedEdge', - separator: true, - regex: tokens.directedEdge, - populate: function populate(selector, query) { - var edgeQuery = newQuery(); - var source = query; - var target = newQuery(); +var LayeredTextureCache = function LayeredTextureCache(renderer, eleTxrCache) { + var self = this; - edgeQuery.group = 'edges'; - edgeQuery.target = target; - edgeQuery.source = source; - edgeQuery.subject = selector.currentSubject; + var r = self.renderer = renderer; - // the query in the selector should be the edge rather than the source - replaceLastQuery(selector, query, edgeQuery); + self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ] - // we're now populating the target query with expressions that follow - return target; - } -}, { - name: 'undirectedEdge', - separator: true, - regex: tokens.undirectedEdge, - populate: function populate(selector, query) { - var edgeQuery = newQuery(); - var source = query; - var target = newQuery(); + self.firstGet = true; - edgeQuery.group = 'edges'; - edgeQuery.connectedNodes = [source, target]; - edgeQuery.subject = selector.currentSubject; + self.lastInvalidationTime = util.performanceNow() - 2 * invalidThreshold; - // the query in the selector should be the edge rather than the source - replaceLastQuery(selector, query, edgeQuery); + self.skipping = false; - // we're now populating the target query with expressions that follow - return target; - } -}, { - name: 'child', - separator: true, - regex: tokens.child, - populate: function populate(selector, query) { - // this query is the parent of the following query - var childQuery = newQuery(); - childQuery.parent = query; - childQuery.subject = selector.currentSubject; + r.beforeRender(function (willDraw, now) { + if (now - self.lastInvalidationTime <= invalidThreshold) { + self.skipping = true; + } else { + self.skipping = false; + } + }); - // it's cheaper to compare children first and go up so replace the parent - replaceLastQuery(selector, query, childQuery); + var qSort = function qSort(a, b) { + return b.reqs - a.reqs; + }; - // we're now populating the child query with expressions that follow - return childQuery; - } -}, { - name: 'descendant', - separator: true, - regex: tokens.descendant, - populate: function populate(selector, query) { - // this query is the ancestor of the following query - var descendantQuery = newQuery(); - descendantQuery.ancestor = query; - descendantQuery.subject = selector.currentSubject; + self.layersQueue = new Heap(qSort); - // it's cheaper to compare descendants first and go up so replace the ancestor - replaceLastQuery(selector, query, descendantQuery); + self.eleTxrCache = eleTxrCache; - // we're now populating the descendant query with expressions that follow - return descendantQuery; - } -}, { - name: 'subject', - modifier: true, - regex: tokens.subject, - populate: function populate(selector, query) { - if (selector.currentSubject != null && query.subject != query) { - util.error('Redefinition of subject in selector `' + selector.toString() + '`'); - return false; - } + self.setupEleCacheInvalidation(); - selector.currentSubject = query; - query.subject = query; - selector[selector.length - 1].subject = query; - } -}]; + self.setupDequeueing(); +}; -exprs.forEach(function (e) { - return e.regexObj = new RegExp('^' + e.regex); -}); +var LTCp = LayeredTextureCache.prototype; -module.exports = exprs; +var layerIdPool = 0; +var MAX_INT = Math.pow(2, 53) - 1; -/***/ }), -/* 119 */ -/***/ (function(module, exports, __webpack_require__) { +LTCp.makeLayer = function (bb, lvl) { + var scale = Math.pow(2, lvl); -"use strict"; + var w = Math.ceil(bb.w * scale); + var h = Math.ceil(bb.h * scale); + var canvas = document.createElement('canvas'); // eslint-disable-line no-undef -var _require = __webpack_require__(17), - stateSelectorMatches = _require.stateSelectorMatches; + canvas.width = w; + canvas.height = h; -var is = __webpack_require__(0); + var layer = { + id: layerIdPool = ++layerIdPool % MAX_INT, + bb: bb, + level: lvl, + width: w, + height: h, + canvas: canvas, + context: canvas.getContext('2d'), + eles: [], + elesQueue: [], + reqs: 0 + }; -// generic checking for data/metadata -var operandsMatch = function operandsMatch(query, params) { - var allDataMatches = true; - for (var k = 0; k < query[params.name].length; k++) { - var data = query[params.name][k]; - var operator = data.operator; - var value = data.value; - var field = data.field; - var _matches = void 0; - var fieldVal = params.fieldValue(field); + // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level); - if (operator != null && value != null) { - var fieldStr = !is.string(fieldVal) && !is.number(fieldVal) ? '' : '' + fieldVal; - var valStr = '' + value; + var cxt = layer.context; + var dx = -layer.bb.x1; + var dy = -layer.bb.y1; - var caseInsensitive = false; - if (operator.indexOf('@') >= 0) { - fieldStr = fieldStr.toLowerCase(); - valStr = valStr.toLowerCase(); + // do the transform on creation to save cycles (it's the same for all eles) + cxt.scale(scale, scale); + cxt.translate(dx, dy); - operator = operator.replace('@', ''); - caseInsensitive = true; - } + return layer; +}; - var notExpr = false; - if (operator.indexOf('!') >= 0) { - operator = operator.replace('!', ''); - notExpr = true; - } +LTCp.getLayers = function (eles, pxRatio, lvl) { + var self = this; + var r = self.renderer; + var cy = r.cy; + var zoom = cy.zoom(); + var firstGet = self.firstGet; - // if we're doing a case insensitive comparison, then we're using a STRING comparison - // even if we're comparing numbers - if (caseInsensitive) { - value = valStr.toLowerCase(); - fieldVal = fieldStr.toLowerCase(); - } + self.firstGet = false; - var isIneqCmp = false; + // log('--\nget layers with %s eles', eles.length); + //log eles.map(function(ele){ return ele.id() }) ); - switch (operator) { - case '*=': - _matches = fieldStr.indexOf(valStr) >= 0; - break; - case '$=': - _matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0; - break; - case '^=': - _matches = fieldStr.indexOf(valStr) === 0; - break; - case '=': - _matches = fieldVal === value; - break; - case '>': - isIneqCmp = true; - _matches = fieldVal > value; - break; - case '>=': - isIneqCmp = true; - _matches = fieldVal >= value; - break; - case '<': - isIneqCmp = true; - _matches = fieldVal < value; - break; - case '<=': - isIneqCmp = true; - _matches = fieldVal <= value; - break; - default: - _matches = false; - break; - } + if (lvl == null) { + lvl = Math.ceil(math.log2(zoom * pxRatio)); - // apply the not op, but null vals for inequalities should always stay non-matching - if (notExpr && (fieldVal != null || !isIneqCmp)) { - _matches = !_matches; + if (lvl < minLvl) { + lvl = minLvl; + } else if (zoom >= maxZoom || lvl > maxLvl) { + return null; + } + } + + self.validateLayersElesOrdering(lvl, eles); + + var layersByLvl = self.layersByLevel; + var scale = Math.pow(2, lvl); + var layers = layersByLvl[lvl] = layersByLvl[lvl] || []; + var bb; + + var lvlComplete = self.levelIsComplete(lvl, eles); + var tmpLayers; + + var checkTempLevels = function checkTempLevels() { + var canUseAsTmpLvl = function canUseAsTmpLvl(l) { + self.validateLayersElesOrdering(l, eles); + + if (self.levelIsComplete(l, eles)) { + tmpLayers = layersByLvl[l]; + return true; } - } else if (operator != null) { - switch (operator) { - case '?': - _matches = fieldVal ? true : false; - break; - case '!': - _matches = fieldVal ? false : true; - break; - case '^': - _matches = fieldVal === undefined; - break; + }; + + var checkLvls = function checkLvls(dir) { + if (tmpLayers) { + return; } - } else { - _matches = fieldVal !== undefined; - } - if (!_matches) { - allDataMatches = false; - break; - } - } // for + for (var l = lvl + dir; minLvl <= l && l <= maxLvl; l += dir) { + if (canUseAsTmpLvl(l)) { + break; + } + } + }; - return allDataMatches; -}; // operandsMatch + checkLvls(+1); + checkLvls(-1); -// check parent/child relations -var confirmRelations = function confirmRelations(query, isNecessary, eles) { - if (query != null) { - var _matches2 = false; + // remove the invalid layers; they will be replaced as needed later in this function + for (var i = layers.length - 1; i >= 0; i--) { + var layer = layers[i]; - if (!isNecessary) { - return false; + if (layer.invalid) { + util.removeFromArray(layers, layer); + } } + }; - eles = eles(); // save cycles if query == null + if (!lvlComplete) { + // if the current level is incomplete, then use the closest, best quality layerset temporarily + // and later queue the current layerset so we can get the proper quality level soon - // query must match for at least one element (may be recursive) - for (var i = 0; i < eles.length; i++) { - if (queryMatches(query, eles[i])) { - _matches2 = true; - break; + checkTempLevels(); + } else { + // log('level complete, using existing layers\n--'); + return layers; + } + + var getBb = function getBb() { + if (!bb) { + bb = math.makeBoundingBox(); + + for (var i = 0; i < eles.length; i++) { + math.updateBoundingBox(bb, eles[i].boundingBox()); } } - return _matches2; - } else { - return true; - } -}; + return bb; + }; -var queryMatches = function queryMatches(query, ele) { - // make single group-only selectors really cheap to check since they're the most common ones - if (query.groupOnly) { - return query.group === '*' || query.group === ele.group(); - } + var makeLayer = function makeLayer(opts) { + opts = opts || {}; - // check group - if (query.group != null && query.group != '*' && query.group != ele.group()) { - return false; - } + var after = opts.after; - var cy = ele.cy(); - var k = void 0; + getBb(); - // check colon selectors - var allColonSelectorsMatch = true; - for (k = 0; k < query.colonSelectors.length; k++) { - var sel = query.colonSelectors[k]; + var area = bb.w * scale * (bb.h * scale); - allColonSelectorsMatch = stateSelectorMatches(sel, ele); + if (area > maxLayerArea) { + return null; + } - if (!allColonSelectorsMatch) break; - } - if (!allColonSelectorsMatch) return false; + var layer = self.makeLayer(bb, lvl); - // check id - var allIdsMatch = true; - for (k = 0; k < query.ids.length; k++) { - var id = query.ids[k]; - var actualId = ele.id(); + if (after != null) { + var index = layers.indexOf(after) + 1; - allIdsMatch = allIdsMatch && id == actualId; + layers.splice(index, 0, layer); + } else if (opts.insert === undefined || opts.insert) { + // no after specified => first layer made so put at start + layers.unshift(layer); + } - if (!allIdsMatch) break; + // if( tmpLayers ){ + //self.queueLayer( layer ); + // } + + return layer; + }; + + if (self.skipping && !firstGet) { + // log('skip layers'); + return null; } - if (!allIdsMatch) return false; - // check classes - var allClassesMatch = true; - for (k = 0; k < query.classes.length; k++) { - var cls = query.classes[k]; + // log('do layers'); - allClassesMatch = allClassesMatch && ele.hasClass(cls); + var layer = null; + var maxElesPerLayer = eles.length / defNumLayers; + var allowLazyQueueing = alwaysQueue && !firstGet; - if (!allClassesMatch) break; - } - if (!allClassesMatch) return false; + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var rs = ele._private.rscratch; + var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; - // check data matches - var allDataMatches = operandsMatch(query, { - name: 'data', - fieldValue: function fieldValue(field) { - return ele.data(field); - } - }); + // log('look at ele', ele.id()); - if (!allDataMatches) { - return false; - } + var existingLayer = caches[lvl]; - // check metadata matches - var allMetaMatches = operandsMatch(query, { - name: 'meta', - fieldValue: function fieldValue(field) { - return ele[field](); + if (existingLayer) { + // reuse layer for later eles + // log('reuse layer for', ele.id()); + layer = existingLayer; + continue; } - }); - if (!allMetaMatches) { - return false; - } + if (!layer || layer.eles.length >= maxElesPerLayer || !math.boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) { + // log('make new layer for ele %s', ele.id()); - // check collection - if (query.collection != null) { - var matchesAny = query.collection.hasElementWithId(ele.id()); + layer = makeLayer({ insert: true, after: layer }); - if (!matchesAny) { - return false; + // if now layer can be built then we can't use layers at this level + if (!layer) { + return null; + } + + // log('new layer with id %s', layer.id); } - } - // check filter function - if (query.filter != null && ele.collection().some(query.filter)) { - return false; - } + if (tmpLayers || allowLazyQueueing) { + // log('queue ele %s in layer %s', ele.id(), layer.id); + self.queueLayer(layer, ele); + } else { + // log('draw ele %s in layer %s', ele.id(), layer.id); + self.drawEleInLayer(layer, ele, lvl, pxRatio); + } - var isCompound = cy.hasCompoundNodes(); - var getSource = function getSource() { - return ele.source(); - }; - var getTarget = function getTarget() { - return ele.target(); - }; + layer.eles.push(ele); - if (!confirmRelations(query.parent, isCompound, function () { - return ele.parent(); - })) { - return false; + caches[lvl] = layer; } - if (!confirmRelations(query.ancestor, isCompound, function () { - return ele.parents(); - })) { - return false; - } + // log('--'); - if (!confirmRelations(query.child, isCompound, function () { - return ele.children(); - })) { - return false; + if (tmpLayers) { + // then we only queued the current layerset and can't draw it yet + return tmpLayers; } - if (!confirmRelations(query.descendant, isCompound, function () { - return ele.descendants(); - })) { - return false; + if (allowLazyQueueing) { + // log('lazy queue level', lvl); + return null; } - if (!confirmRelations(query.source, true, getSource)) { - return false; + return layers; +}; + +// a layer may want to use an ele cache of a higher level to avoid blurriness +// so the layer level might not equal the ele level +LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) { + return lvl; +}; + +function imgSmoothing(context, bool) { + if (context.imageSmoothingEnabled != null) { + context.imageSmoothingEnabled = bool; + } else { + context.webkitImageSmoothingEnabled = bool; + context.mozImageSmoothingEnabled = bool; + context.msImageSmoothingEnabled = bool; } +} - if (!confirmRelations(query.target, true, getTarget)) { - return false; +LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) { + var self = this; + var r = this.renderer; + var context = layer.context; + var bb = ele.boundingBox(); + + if (bb.w === 0 || bb.h === 0 || !ele.visible()) { + return; } - if (query.connectedNodes) { - var q0 = query.connectedNodes[0]; - var q1 = query.connectedNodes[1]; + var eleCache = self.eleTxrCache; + var reason = useHighQualityEleTxrReqs ? eleCache.reasons.highQuality : undefined; + + lvl = self.getEleLevelForLayerLevel(lvl, pxRatio); + + var cache = useEleTxrCaching ? eleCache.getElement(ele, bb, null, lvl, reason) : null; + + if (cache) { + if (disableEleImgSmoothing) { + imgSmoothing(context, false); + } + + context.drawImage(cache.texture.canvas, cache.x, 0, cache.width, cache.height, bb.x1, bb.y1, bb.w, bb.h); - if (confirmRelations(q0, true, getSource) && confirmRelations(q1, true, getTarget)) { - // match - } else if (confirmRelations(q0, true, getTarget) && confirmRelations(q1, true, getSource)) { - // match - } else { - return false; + if (disableEleImgSmoothing) { + imgSmoothing(context, true); } + } else { + // if the element is not cacheable, then draw directly + r.drawElement(context, ele); } +}; - // we've reached the end, so we've matched everything for this query - return true; -}; // queryMatches - -// filter an existing collection -var filter = function filter(collection) { +LTCp.levelIsComplete = function (lvl, eles) { var self = this; - var cy = collection.cy(); + var layers = self.layersByLevel[lvl]; - // don't bother trying if it's invalid - if (self.invalid()) { - return cy.collection(); + if (!layers || layers.length === 0) { + return false; } - // for 1 id #foo queries, just get the element - if (self.length === 1 && self[0].length === 1 && self[0].ids.length === 1) { - return collection.getElementById(self[0].ids[0]).collection(); - } + var numElesInLayers = 0; - var selectorFunction = function selectorFunction(element) { - for (var j = 0; j < self.length; j++) { - var query = self[j]; + for (var i = 0; i < layers.length; i++) { + var layer = layers[i]; - if (queryMatches(query, element)) { - return true; - } + // if there are any eles needed to be drawn yet, the level is not complete + if (layer.reqs > 0) { + return false; } - return false; - }; + // if the layer is invalid, the level is not complete + if (layer.invalid) { + return false; + } - if (self.text() == null) { - selectorFunction = function selectorFunction() { - return true; - }; + numElesInLayers += layer.eles.length; } - var filteredCollection = collection.filter(selectorFunction); + // we should have exactly the number of eles passed in to be complete + if (numElesInLayers !== eles.length) { + return false; + } - return filteredCollection; -}; // filter + return true; +}; -// does selector match a single element? -var matches = function matches(ele) { - var self = this; +LTCp.validateLayersElesOrdering = function (lvl, eles) { + var layers = this.layersByLevel[lvl]; - // don't bother trying if it's invalid - if (self.invalid()) { - return false; + if (!layers) { + return; } - for (var j = 0; j < self.length; j++) { - var query = self[j]; + // if in a layer the eles are not in the same order, then the layer is invalid + // (i.e. there is an ele in between the eles in the layer) - if (queryMatches(query, ele)) { - return true; - } - } + for (var i = 0; i < layers.length; i++) { + var layer = layers[i]; + var offset = -1; - return false; -}; // filter + // find the offset + for (var j = 0; j < eles.length; j++) { + if (layer.eles[0] === eles[j]) { + offset = j; + break; + } + } -module.exports = { matches: matches, filter: filter }; + if (offset < 0) { + // then the layer has nonexistant elements and is invalid + this.invalidateLayer(layer); + continue; + } -/***/ }), -/* 120 */ -/***/ (function(module, exports, __webpack_require__) { + // the eles in the layer must be in the same continuous order, else the layer is invalid -"use strict"; + var o = offset; + for (var j = 0; j < layer.eles.length; j++) { + if (layer.eles[j] !== eles[o + j]) { + // log('invalidate based on ordering', layer.id); -var util = __webpack_require__(1); -var exprs = __webpack_require__(118); -var newQuery = __webpack_require__(11); + this.invalidateLayer(layer); + break; + } + } + } +}; -// of all the expressions, find the first match in the remaining text -var consumeExpr = function consumeExpr(remaining) { - var expr = void 0; - var match = void 0; - var name = void 0; +LTCp.updateElementsInLayers = function (eles, update) { + var self = this; + var isEles = is.element(eles[0]); - for (var j = 0; j < exprs.length; j++) { - var e = exprs[j]; - var n = e.name; + // collect udpated elements (cascaded from the layers) and update each + // layer itself along the way + for (var i = 0; i < eles.length; i++) { + var req = isEles ? null : eles[i]; + var ele = isEles ? eles[i] : eles[i].ele; + var rs = ele._private.rscratch; + var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; - var m = remaining.match(e.regexObj); + for (var l = minLvl; l <= maxLvl; l++) { + var layer = caches[l]; - if (m != null) { - match = m; - expr = e; - name = n; + if (!layer) { + continue; + } - var consumed = m[0]; - remaining = remaining.substring(consumed.length); + // if update is a request from the ele cache, then it affects only + // the matching level + if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) { + continue; + } - break; // we've consumed one expr, so we can return now + update(layer, ele, req); } } - - return { - expr: expr, - match: match, - name: name, - remaining: remaining - }; }; -// consume all leading whitespace -var consumeWhitespace = function consumeWhitespace(remaining) { - var match = remaining.match(/^\s+/); +LTCp.haveLayers = function () { + var self = this; + var haveLayers = false; - if (match) { - var consumed = match[0]; - remaining = remaining.substring(consumed.length); + for (var l = minLvl; l <= maxLvl; l++) { + var layers = self.layersByLevel[l]; + + if (layers && layers.length > 0) { + haveLayers = true; + break; + } } - return remaining; + return haveLayers; }; -var parse = function parse(selector) { +LTCp.invalidateElements = function (eles) { var self = this; - var remaining = self._private.selectorText = selector; - - var currentQuery = self[0] = newQuery(); - self.length = 1; - - remaining = consumeWhitespace(remaining); // get rid of leading whitespace - - for (;;) { - var check = consumeExpr(remaining); + self.lastInvalidationTime = util.performanceNow(); - if (check.expr == null) { - util.error('The selector `' + selector + '`is invalid'); - return false; - } else { - var args = check.match.slice(1); + // log('update invalidate layer time from eles'); - // let the token populate the selector object in currentQuery - var ret = check.expr.populate(self, currentQuery, args); + if (eles.length === 0 || !self.haveLayers()) { + return; + } - if (ret === false) { - return false; // exit if population failed - } else if (ret != null) { - currentQuery = ret; // change the current query to be filled if the expr specifies - } - } + self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) { + self.invalidateLayer(layer); + }); +}; - remaining = check.remaining; +LTCp.invalidateLayer = function (layer) { + // log('update invalidate layer time'); - // we're done when there's nothing left to parse - if (remaining.match(/^\s*$/)) { - break; - } - } + this.lastInvalidationTime = util.performanceNow(); - // adjust references for subject - for (var j = 0; j < self.length; j++) { - var query = self[j]; + if (layer.invalid) { + return; + } // save cycles - if (query.subject != null) { - // go up the tree until we reach the subject - for (;;) { - if (query.subject === query) { - break; - } // done if subject is self + var lvl = layer.level; + var eles = layer.eles; + var layers = this.layersByLevel[lvl]; - if (query.parent != null) { - // swap parent/child reference - var parent = query.parent; - var child = query; + // log('invalidate layer', layer.id ); - child.parent = null; - parent.child = child; + util.removeFromArray(layers, layer); + // layer.eles = []; - query = parent; // go up the tree - } else if (query.ancestor != null) { - // swap ancestor/descendant - var ancestor = query.ancestor; - var descendant = query; + layer.elesQueue = []; - descendant.ancestor = null; - ancestor.descendant = descendant; + layer.invalid = true; - query = ancestor; // go up the tree - } else if (query.source || query.target || query.connectedNodes) { - util.error('The selector `' + self.text() + '` can not contain a subject selector that applies to the source or target of an edge selector'); - return false; - } else { - util.error('When adjusting references for the selector `' + self.text() + '`, neither parent nor ancestor was found'); - return false; - } - } // for + if (layer.replacement) { + layer.replacement.invalid = true; + } - self[j] = query.subject; // subject should be the root query - } // if - } // for + for (var i = 0; i < eles.length; i++) { + var caches = eles[i]._private.rscratch.imgLayerCaches; - return true; // success + if (caches) { + caches[lvl] = null; + } + } }; -module.exports = { parse: parse }; +LTCp.refineElementTextures = function (eles) { + var self = this; -/***/ }), -/* 121 */ -/***/ (function(module, exports, __webpack_require__) { + // log('refine', eles.length); -"use strict"; + self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) { + var rLyr = layer.replacement; + if (!rLyr) { + rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level); + rLyr.replaces = layer; + rLyr.eles = layer.eles; -var util = __webpack_require__(1); + // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level); + } -// tokens in the query language -var tokens = { - metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]', // chars we need to escape in let names, etc - comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=', // binary comparison op (used in data selectors) - boolOp: '\\?|\\!|\\^', // boolean (unary) operators (used in data selectors) - string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'", // string literals (used in data selectors) -- doublequotes | singlequotes - number: util.regex.number, // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123 - meta: 'degree|indegree|outdegree', // allowed metadata fields (i.e. allowed functions to use from Collection) - separator: '\\s*,\\s*', // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass - descendant: '\\s+', - child: '\\s+>\\s+', - subject: '\\$', - group: 'node|edge|\\*', - directedEdge: '\\s+->\\s+', - undirectedEdge: '\\s+<->\\s+' + if (!rLyr.reqs) { + for (var i = 0; i < rLyr.eles.length; i++) { + self.queueLayer(rLyr, rLyr.eles[i]); + } + + // log('queue replacement layer refinement', rLyr.id); + } + }); }; -tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name -tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number -tokens.className = tokens.variable; // a class name (follows variable conventions) -tokens.id = tokens.variable; // an element id (follows variable conventions) -(function () { - var ops = void 0, - op = void 0, - i = void 0; +LTCp.setupEleCacheInvalidation = function () { + var self = this; + var eleDeqs = []; - // add @ variants to comparatorOp - ops = tokens.comparatorOp.split('|'); - for (i = 0; i < ops.length; i++) { - op = ops[i]; - tokens.comparatorOp += '|@' + op; + if (!useEleTxrCaching) { + return; } - // add ! variants to comparatorOp - ops = tokens.comparatorOp.split('|'); - for (i = 0; i < ops.length; i++) { - op = ops[i]; - - if (op.indexOf('!') >= 0) { - continue; - } // skip ops that explicitly contain ! - if (op === '=') { - continue; - } // skip = b/c != is explicitly defined + var updatedElesInLayers = util.debounce(function () { + self.refineElementTextures(eleDeqs); - tokens.comparatorOp += '|\\!' + op; - } -})(); + eleDeqs = []; + }, refineEleDebounceTime); -module.exports = tokens; + self.eleTxrCache.onDequeue(function (reqs) { + for (var i = 0; i < reqs.length; i++) { + eleDeqs.push(reqs[i]); + } -/***/ }), -/* 122 */ -/***/ (function(module, exports, __webpack_require__) { + updatedElesInLayers(); + }); +}; -"use strict"; +LTCp.queueLayer = function (layer, ele) { + var self = this; + var q = self.layersQueue; + var elesQ = layer.elesQueue; + var hasId = elesQ.hasId = elesQ.hasId || {}; + // if a layer is going to be replaced, queuing is a waste of time + if (layer.replacement) { + return; + } -var util = __webpack_require__(1); -var is = __webpack_require__(0); -var Promise = __webpack_require__(5); + if (ele) { + if (hasId[ele.id()]) { + return; + } -var styfn = {}; + elesQ.push(ele); + hasId[ele.id()] = true; + } -// (potentially expensive calculation) -// apply the style to the element based on -// - its bypass -// - what selectors match it -styfn.apply = function (eles) { - var self = this; - var _p = self._private; - var cy = _p.cy; - var updatedEles = cy.collection(); + if (layer.reqs) { + layer.reqs++; - if (_p.newStyle) { - // clear style caches - _p.contextStyles = {}; - _p.propDiffs = {}; + q.updateItem(layer); + } else { + layer.reqs = 1; - self.cleanElements(eles, true); + q.push(layer); } +}; - for (var ie = 0; ie < eles.length; ie++) { - var ele = eles[ie]; +LTCp.dequeue = function (pxRatio) { + var self = this; + var q = self.layersQueue; + var deqd = []; + var eleDeqs = 0; - var cxtMeta = self.getContextMeta(ele); + while (eleDeqs < maxDeqSize) { + if (q.size() === 0) { + break; + } - if (cxtMeta.empty) { + var layer = q.peek(); + + // if a layer has been or will be replaced, then don't waste time with it + if (layer.replacement) { + // log('layer %s in queue skipped b/c it already has a replacement', layer.id); + q.pop(); continue; - } else { - updatedEles.merge(ele); } - var cxtStyle = self.getContextStyle(cxtMeta); - var app = self.applyContextStyle(cxtMeta, cxtStyle, ele); + // if this is a replacement layer that has been superceded, then forget it + if (layer.replaces && layer !== layer.replaces.replacement) { + // log('layer is no longer the most uptodate replacement; dequeued', layer.id) + q.pop(); + continue; + } - if (!_p.newStyle) { - self.updateTransitions(ele, app.diffProps); + if (layer.invalid) { + // log('replacement layer %s is invalid; dequeued', layer.id); + q.pop(); + continue; } - self.updateStyleHints(ele); - } // for elements + var ele = layer.elesQueue.shift(); - _p.newStyle = false; + if (ele) { + // log('dequeue layer %s', layer.id); - return updatedEles; -}; + self.drawEleInLayer(layer, ele, layer.level, pxRatio); -styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) { - var self = this; - var cache = self._private.propDiffs = self._private.propDiffs || {}; - var dualCxtKey = oldCxtKey + '-' + newCxtKey; - var cachedVal = cache[dualCxtKey]; + eleDeqs++; + } - if (cachedVal) { - return cachedVal; - } + if (deqd.length === 0) { + // we need only one entry in deqd to queue redrawing etc + deqd.push(true); + } - var diffProps = []; - var addedProp = {}; + // if the layer has all its eles done, then remove from the queue + if (layer.elesQueue.length === 0) { + q.pop(); - for (var i = 0; i < self.length; i++) { - var cxt = self[i]; - var oldHasCxt = oldCxtKey[i] === 't'; - var newHasCxt = newCxtKey[i] === 't'; - var cxtHasDiffed = oldHasCxt !== newHasCxt; - var cxtHasMappedProps = cxt.mappedProperties.length > 0; + layer.reqs = 0; - if (cxtHasDiffed || cxtHasMappedProps) { - var props = void 0; + // log('dequeue of layer %s complete', layer.id); - if (cxtHasDiffed && cxtHasMappedProps) { - props = cxt.properties; // suffices b/c mappedProperties is a subset of properties - } else if (cxtHasDiffed) { - props = cxt.properties; // need to check them all - } else if (cxtHasMappedProps) { - props = cxt.mappedProperties; // only need to check mapped + // when a replacement layer is dequeued, it replaces the old layer in the level + if (layer.replaces) { + self.applyLayerReplacement(layer); } - for (var j = 0; j < props.length; j++) { - var prop = props[j]; - var name = prop.name; + self.requestRedraw(); + } + } - // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter - // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result - // is cached) - var laterCxtOverrides = false; - for (var k = i + 1; k < self.length; k++) { - var laterCxt = self[k]; - var hasLaterCxt = newCxtKey[k] === 't'; + return deqd; +}; + +LTCp.applyLayerReplacement = function (layer) { + var self = this; + var layersInLevel = self.layersByLevel[layer.level]; + var replaced = layer.replaces; + var index = layersInLevel.indexOf(replaced); + + // if the replaced layer is not in the active list for the level, then replacing + // refs would be a mistake (i.e. overwriting the true active layer) + if (index < 0 || replaced.invalid) { + // log('replacement layer would have no effect', layer.id); + return; + } - if (!hasLaterCxt) { - continue; - } // can't override unless the context is active + layersInLevel[index] = layer; // replace level ref - laterCxtOverrides = laterCxt.properties[prop.name] != null; + // replace refs in eles + for (var i = 0; i < layer.eles.length; i++) { + var _p = layer.eles[i]._private; + var cache = _p.imgLayerCaches = _p.imgLayerCaches || {}; - if (laterCxtOverrides) { - break; - } // exit early as long as one later context overrides - } + if (cache) { + cache[layer.level] = layer; + } + } - if (!addedProp[name] && !laterCxtOverrides) { - addedProp[name] = true; - diffProps.push(name); - } - } // for props - } // if - } // for contexts + // log('apply replacement layer %s over %s', layer.id, replaced.id); - cache[dualCxtKey] = diffProps; - return diffProps; + self.requestRedraw(); }; -styfn.getContextMeta = function (ele) { - var self = this; - var cxtKey = ''; - var diffProps = void 0; - var prevKey = ele._private.styleCxtKey || ''; +LTCp.requestRedraw = util.debounce(function () { + var r = this.renderer; - if (self._private.newStyle) { - prevKey = ''; // since we need to apply all style if a fresh stylesheet + r.redrawHint('eles', true); + r.redrawHint('drag', true); + r.redraw(); +}, 100); + +LTCp.setupDequeueing = defs.setupDequeueing({ + deqRedrawThreshold: deqRedrawThreshold, + deqCost: deqCost, + deqAvgCost: deqAvgCost, + deqNoDrawCost: deqNoDrawCost, + deqFastCost: deqFastCost, + deq: function deq(self, pxRatio) { + return self.dequeue(pxRatio); + }, + onDeqd: util.noop, + shouldRedraw: util.trueify, + priority: function priority(self) { + return self.renderer.beforeRenderPriorities.lyrTxrDeq; } +}); - // get the cxt key - for (var i = 0; i < self.length; i++) { - var context = self[i]; - var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core' +module.exports = LayeredTextureCache; - if (contextSelectorMatches) { - cxtKey += 't'; - } else { - cxtKey += 'f'; - } - } // for context +/***/ }), +/* 126 */ +/***/ (function(module, exports, __webpack_require__) { - diffProps = self.getPropertiesDiff(prevKey, cxtKey); +"use strict"; - ele._private.styleCxtKey = cxtKey; - return { - key: cxtKey, - diffPropNames: diffProps, - empty: diffProps.length === 0 - }; -}; +var CRp = {}; -// gets a computed ele style object based on matched contexts -styfn.getContextStyle = function (cxtMeta) { - var cxtKey = cxtMeta.key; - var self = this; - var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; +var impl; - // if already computed style, returned cached copy - if (cxtStyles[cxtKey]) { - return cxtStyles[cxtKey]; - } +CRp.arrowShapeImpl = function (name) { + return (impl || (impl = { + 'polygon': function polygon(context, points) { + for (var i = 0; i < points.length; i++) { + var pt = points[i]; - var style = { - _private: { - key: cxtKey - } - }; + context.lineTo(pt.x, pt.y); + } + }, - for (var i = 0; i < self.length; i++) { - var cxt = self[i]; - var hasCxt = cxtKey[i] === 't'; + 'triangle-backcurve': function triangleBackcurve(context, points, controlPoint) { + var firstPt; - if (!hasCxt) { - continue; - } + for (var i = 0; i < points.length; i++) { + var pt = points[i]; - for (var j = 0; j < cxt.properties.length; j++) { - var prop = cxt.properties[j]; + if (i === 0) { + firstPt = pt; + } - style[prop.name] = prop; - } - } + context.lineTo(pt.x, pt.y); + } - cxtStyles[cxtKey] = style; - return style; -}; + context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y); + }, -styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) { - var self = this; - var diffProps = cxtMeta.diffPropNames; - var retDiffProps = {}; + 'triangle-tee': function triangleTee(context, trianglePoints, teePoints) { + if (context.beginPath) { + context.beginPath(); + } - for (var i = 0; i < diffProps.length; i++) { - var diffPropName = diffProps[i]; - var cxtProp = cxtStyle[diffPropName]; - var eleProp = ele.pstyle(diffPropName); + var triPts = trianglePoints; + for (var i = 0; i < triPts.length; i++) { + var pt = triPts[i]; - if (!cxtProp) { - // no context prop means delete - if (!eleProp) { - continue; // no existing prop means nothing needs to be removed - // nb affects initial application on mapped values like control-point-distances - } else if (eleProp.bypass) { - cxtProp = { name: diffPropName, deleteBypassed: true }; - } else { - cxtProp = { name: diffPropName, delete: true }; + context.lineTo(pt.x, pt.y); } - } - // save cycles when the context prop doesn't need to be applied - if (eleProp === cxtProp) { - continue; - } + if (context.closePath) { + context.closePath(); + } - var retDiffProp = retDiffProps[diffPropName] = { - prev: eleProp - }; + if (context.beginPath) { + context.beginPath(); + } - self.applyParsedProperty(ele, cxtProp); + var teePts = teePoints; + var firstTeePt = teePoints[0]; + context.moveTo(firstTeePt.x, firstTeePt.y); - retDiffProp.next = ele.pstyle(diffPropName); + for (var i = 0; i < teePts.length; i++) { + var pt = teePts[i]; - if (retDiffProp.next && retDiffProp.next.bypass) { - retDiffProp.next = retDiffProp.next.bypassed; - } - } + context.lineTo(pt.x, pt.y); + } + if (context.closePath) { + context.closePath(); + } + }, - return { - diffProps: retDiffProps - }; -}; + 'triangle-cross': function triangleCross(context, trianglePoints, crossLinePoints) { + if (context.beginPath) { + context.beginPath(); + } -styfn.updateStyleHints = function (ele) { - var _p = ele._private; - var self = this; + var triPts = trianglePoints; + for (var i = 0; i < triPts.length; i++) { + var pt = triPts[i]; - if (ele.removed()) { - return; - } + context.lineTo(pt.x, pt.y); + } - // set whether has pie or not; for greater efficiency - var hasPie = false; - if (_p.group === 'nodes') { - for (var i = 1; i <= self.pieBackgroundN; i++) { - // 1..N - var _size = ele.pstyle('pie-' + i + '-background-size').value; + if (context.closePath) { + context.closePath(); + } - if (_size > 0) { - hasPie = true; - break; + if (context.beginPath) { + context.beginPath(); } - } - } - _p.hasPie = hasPie; + var teePts = crossLinePoints; + var firstTeePt = crossLinePoints[0]; + context.moveTo(firstTeePt.x, firstTeePt.y); - var transform = ele.pstyle('text-transform').strValue; - var content = ele.pstyle('label').strValue; - var srcContent = ele.pstyle('source-label').strValue; - var tgtContent = ele.pstyle('target-label').strValue; - var fStyle = ele.pstyle('font-style').strValue; - var size = ele.pstyle('font-size').pfValue + 'px'; - var family = ele.pstyle('font-family').strValue; - // let letiant = style['font-letiant'].strValue; - var weight = ele.pstyle('font-weight').strValue; - var valign = ele.pstyle('text-valign').strValue; - var halign = ele.pstyle('text-valign').strValue; - var oWidth = ele.pstyle('text-outline-width').pfValue; - var wrap = ele.pstyle('text-wrap').strValue; - var wrapW = ele.pstyle('text-max-width').pfValue; - var labelStyleKey = fStyle + '$' + size + '$' + family + '$' + weight + '$' + transform + '$' + valign + '$' + halign + '$' + oWidth + '$' + wrap + '$' + wrapW; - _p.labelStyleKey = labelStyleKey; - _p.sourceLabelKey = labelStyleKey + '$' + srcContent; - _p.targetLabelKey = labelStyleKey + '$' + tgtContent; - _p.labelKey = labelStyleKey + '$' + content; - _p.fontKey = fStyle + '$' + weight + '$' + size + '$' + family; + for (var i = 0; i < teePts.length; i++) { + var pt = teePts[i]; + + context.lineTo(pt.x, pt.y); + } + if (context.closePath) { + context.closePath(); + } + }, - _p.styleKey = Date.now(); + 'circle': function circle(context, rx, ry, r) { + context.arc(rx, ry, r, 0, Math.PI * 2, false); + } + }))[name]; }; -// apply a property to the style (for internal use) -// returns whether application was successful -// -// now, this function flattens the property, and here's how: -// -// for parsedProp:{ bypass: true, deleteBypass: true } -// no property is generated, instead the bypass property in the -// element's style is replaced by what's pointed to by the `bypassed` -// field in the bypass property (i.e. restoring the property the -// bypass was overriding) -// -// for parsedProp:{ mapped: truthy } -// the generated flattenedProp:{ mapping: prop } -// -// for parsedProp:{ bypass: true } -// the generated flattenedProp:{ bypassed: parsedProp } -styfn.applyParsedProperty = function (ele, parsedProp) { - var self = this; - var prop = parsedProp; - var style = ele._private.style; - var fieldVal = void 0, - flatProp = void 0; - var types = self.types; - var type = self.properties[prop.name].type; - var propIsBypass = prop.bypass; - var origProp = style[prop.name]; - var origPropIsBypass = origProp && origProp.bypass; - var _p = ele._private; - var flatPropMapping = 'mapping'; +module.exports = CRp; - var checkZOrder = function checkZOrder() { - self.checkZOrderTrigger(ele, prop.name, origProp ? origProp.value : null, prop.value); - }; +/***/ }), +/* 127 */ +/***/ (function(module, exports, __webpack_require__) { - // edges connected to compound nodes can not be haystacks - if (parsedProp.name === 'curve-style' && parsedProp.value === 'haystack' && ele.isEdge() && (ele.isLoop() || ele.source().isParent() || ele.target().isParent())) { - prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass); - } +"use strict"; - if (prop.delete) { - // delete the property and use the default value on falsey value - style[prop.name] = undefined; - checkZOrder(); +var math = __webpack_require__(2); - return true; +var CRp = {}; + +CRp.drawElement = function (context, ele, shiftToOriginWithBb, showLabel) { + var r = this; + + if (ele.isNode()) { + r.drawNode(context, ele, shiftToOriginWithBb, showLabel); + } else { + r.drawEdge(context, ele, shiftToOriginWithBb, showLabel); } +}; - if (prop.deleteBypassed) { - // delete the property that the - if (!origProp) { - checkZOrder(); +CRp.drawCachedElement = function (context, ele, pxRatio, extent) { + var r = this; + var bb = ele.boundingBox(); - return true; // can't delete if no prop - } else if (origProp.bypass) { - // delete bypassed - origProp.bypassed = undefined; + if (bb.w === 0 || bb.h === 0) { + return; + } - checkZOrder(); + if (!extent || math.boundingBoxesIntersect(bb, extent)) { + var cache = r.data.eleTxrCache.getElement(ele, bb, pxRatio); - return true; + if (cache != null) { + context.drawImage(cache.texture.canvas, cache.x, 0, cache.width, cache.height, bb.x1, bb.y1, bb.w, bb.h); } else { - return false; // we're unsuccessful deleting the bypassed + // if the element is not cacheable, then draw directly + r.drawElement(context, ele); } } +}; - // check if we need to delete the current bypass - if (prop.deleteBypass) { - // then this property is just here to indicate we need to delete - if (!origProp) { - checkZOrder(); - - return true; // property is already not defined - } else if (origProp.bypass) { - // then replace the bypass property with the original - // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary) - style[prop.name] = origProp.bypassed; +CRp.drawElements = function (context, eles) { + var r = this; - checkZOrder(); + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - return true; - } else { - return false; // we're unsuccessful deleting the bypass - } + r.drawElement(context, ele); } +}; - var printMappingErr = function printMappingErr() { - util.error('Do not assign mappings to elements without corresponding data (e.g. ele `' + ele.id() + '` for property `' + prop.name + '` with data field `' + prop.field + '`); try a `[' + prop.field + ']` selector to limit scope to elements with `' + prop.field + '` defined'); - }; +CRp.drawCachedElements = function (context, eles, pxRatio, extent) { + var r = this; - // put the property in the style objects - switch (prop.mapped) {// flatten the property if mapped - case types.mapData: - { - // flatten the field (e.g. data.foo.bar) - var fields = prop.field.split('.'); - var _fieldVal = _p.data; + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - for (var i = 0; i < fields.length && _fieldVal; i++) { - var field = fields[i]; - _fieldVal = _fieldVal[field]; - } + r.drawCachedElement(context, ele, pxRatio, extent); + } +}; - var percent = void 0; - if (!is.number(_fieldVal)) { - // then keep the mapping but assume 0% for now - percent = 0; - } else { - percent = (_fieldVal - prop.fieldMin) / (prop.fieldMax - prop.fieldMin); - } +CRp.drawCachedNodes = function (context, eles, pxRatio, extent) { + var r = this; - // make sure to bound percent value - if (percent < 0) { - percent = 0; - } else if (percent > 1) { - percent = 1; - } + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; - if (type.color) { - var r1 = prop.valueMin[0]; - var r2 = prop.valueMax[0]; - var g1 = prop.valueMin[1]; - var g2 = prop.valueMax[1]; - var b1 = prop.valueMin[2]; - var b2 = prop.valueMax[2]; - var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3]; - var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3]; + if (!ele.isNode()) { + continue; + } - var clr = [Math.round(r1 + (r2 - r1) * percent), Math.round(g1 + (g2 - g1) * percent), Math.round(b1 + (b2 - b1) * percent), Math.round(a1 + (a2 - a1) * percent)]; + r.drawCachedElement(context, ele, pxRatio, extent); + } +}; - flatProp = { // colours are simple, so just create the flat property instead of expensive string parsing - bypass: prop.bypass, // we're a bypass if the mapping property is a bypass - name: prop.name, - value: clr, - strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')' - }; - } else if (type.number) { - var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent; - flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping); - } else { - return false; // can only map to colours and numbers - } +CRp.drawLayeredElements = function (context, eles, pxRatio, extent) { + var r = this; - if (!flatProp) { - // if we can't flatten the property, then use the origProp so we still keep the mapping itself - flatProp = this.parse(prop.name, origProp.strValue, prop.bypass, flatPropMapping); - } + var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio); - if (!flatProp) { - printMappingErr(); - } - flatProp.mapping = prop; // keep a reference to the mapping - prop = flatProp; // the flattened (mapped) property is the one we want + if (layers) { + for (var i = 0; i < layers.length; i++) { + var layer = layers[i]; + var bb = layer.bb; - break; + if (bb.w === 0 || bb.h === 0) { + continue; } - // direct mapping - case types.data: - { - // flatten the field (e.g. data.foo.bar) - var _fields = prop.field.split('.'); - var _fieldVal2 = _p.data; + context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h); + } + } else { + // fall back on plain caching if no layers + r.drawCachedElements(context, eles, pxRatio, extent); + } +}; - if (_fieldVal2) { - for (var _i = 0; _i < _fields.length; _i++) { - var _field = _fields[_i]; - _fieldVal2 = _fieldVal2[_field]; - } - } +CRp.drawDebugPoints = function (context, eles) { + var draw = function draw(x, y, color) { + context.fillStyle = color; + context.fillRect(x - 1, y - 1, 3, 3); + }; - flatProp = this.parse(prop.name, _fieldVal2, prop.bypass, flatPropMapping); + for (var i = 0; i < eles.length; i++) { + var ele = eles[i]; + var rs = ele._private.rscratch; - if (!flatProp) { - // if we can't flatten the property, then use the origProp so we still keep the mapping itself - var flatPropVal = origProp ? origProp.strValue : ''; + if (ele.isNode()) { + var p = ele.position(); - flatProp = this.parse(prop.name, flatPropVal, prop.bypass, flatPropMapping); - } + draw(p.x, p.y, 'magenta'); + } else { + var pts = rs.allpts; - if (!flatProp) { - printMappingErr(); - } - flatProp.mapping = prop; // keep a reference to the mapping - prop = flatProp; // the flattened (mapped) property is the one we want + for (var j = 0; j + 1 < pts.length; j += 2) { + var x = pts[j]; + var y = pts[j + 1]; - break; + draw(x, y, 'cyan'); } - case types.fn: - { - var fn = prop.value; - var fnRetVal = fn(ele); + draw(rs.midX, rs.midY, 'yellow'); + } + } +}; + +module.exports = CRp; + +/***/ }), +/* 128 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + - flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping); - flatProp.mapping = prop; // keep a reference to the mapping - prop = flatProp; // the flattened (mapped) property is the one we want +var CRp = {}; - break; - } +CRp.drawEdge = function (context, edge, shiftToOriginWithBb, drawLabel) { + var r = this; + var rs = edge._private.rscratch; + var usePaths = r.usePaths(); - case undefined: - break; // just set the property + // if bezier ctrl pts can not be calculated, then die + if (rs.badLine || isNaN(rs.allpts[0])) { + // isNaN in case edge is impossible and browser bugs (e.g. safari) + return; + } - default: - return false; // not a valid mapping + if (!edge.visible()) { + return; } - // if the property is a bypass property, then link the resultant property to the original one - if (propIsBypass) { - if (origPropIsBypass) { - // then this bypass overrides the existing one - prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass - } else { - // then link the orig prop to the new bypass - prop.bypassed = origProp; - } + var bb = void 0; + if (shiftToOriginWithBb) { + bb = shiftToOriginWithBb; - style[prop.name] = prop; // and set - } else { - // prop is not bypass - if (origPropIsBypass) { - // then keep the orig prop (since it's a bypass) and link to the new prop - origProp.bypassed = prop; - } else { - // then just replace the old prop with the new one - style[prop.name] = prop; - } + context.translate(-bb.x1, -bb.y1); } - checkZOrder(); + var overlayPadding = edge.pstyle('overlay-padding').pfValue; + var overlayWidth = 2 * overlayPadding; + var overlayOpacity = edge.pstyle('overlay-opacity').value; + var overlayColor = edge.pstyle('overlay-color').value; + var lineColor = edge.pstyle('line-color').value; + var opacity = edge.pstyle('opacity').value; + var lineStyle = edge.pstyle('line-style').value; + var edgeWidth = edge.pstyle('width').pfValue; - return true; -}; + var drawLine = function drawLine() { + var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity; -styfn.cleanElements = function (eles, keepBypasses) { - var self = this; - var props = self.properties; + context.lineWidth = edgeWidth; + context.lineCap = 'butt'; - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; + r.strokeStyle(context, lineColor[0], lineColor[1], lineColor[2], strokeOpacity); - if (!keepBypasses) { - ele._private.style = {}; - } else { - var style = ele._private.style; + r.drawEdgePath(edge, context, rs.allpts, lineStyle); + }; - for (var j = 0; j < props.length; j++) { - var prop = props[j]; - var eleProp = style[prop.name]; + var drawOverlay = function drawOverlay() { + var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : overlayOpacity; - if (eleProp) { - if (eleProp.bypass) { - eleProp.bypassed = null; - } else { - style[prop.name] = null; - } - } - } + context.lineWidth = overlayWidth; + + if (rs.edgeType === 'self' && !usePaths) { + context.lineCap = 'butt'; + } else { + context.lineCap = 'round'; } - } -}; -// updates the visual style for all elements (useful for manual style modification after init) -styfn.update = function () { - var cy = this._private.cy; - var eles = cy.mutableElements(); + r.strokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], strokeOpacity); - eles.updateStyle(); -}; + r.drawEdgePath(edge, context, rs.allpts, 'solid'); + }; -// just update the functional properties (i.e. mappings) in the elements' -// styles (less expensive than recalculation) -styfn.updateMappers = function (eles) { - var self = this; - var cy = this._private.cy; - var updatedEles = cy.collection(); + var drawArrows = function drawArrows() { + var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity; - for (var i = 0; i < eles.length; i++) { - // for each ele - var ele = eles[i]; - var style = ele._private.style; - var updatedEle = false; + r.drawArrowheads(context, edge, arrowOpacity); + }; - for (var j = 0; j < self.properties.length; j++) { - // for each prop - var prop = self.properties[j]; - var propInStyle = style[prop.name]; + var drawText = function drawText() { + r.drawElementText(context, edge, drawLabel); + }; - if (propInStyle && propInStyle.mapping) { - var mapping = propInStyle.mapping; + context.lineJoin = 'round'; - this.applyParsedProperty(ele, mapping); // reapply the mapping property + var ghost = edge.pstyle('ghost').value === 'yes'; - updatedEle = true; - } - } + if (ghost) { + var gx = edge.pstyle('ghost-offset-x').pfValue; + var gy = edge.pstyle('ghost-offset-y').pfValue; + var ghostOpacity = edge.pstyle('ghost-opacity').value; + var effectiveGhostOpacity = opacity * ghostOpacity; - if (updatedEle) { - this.updateStyleHints(ele); + context.translate(gx, gy); - updatedEles.merge(ele); - } + drawLine(effectiveGhostOpacity); + drawArrows(effectiveGhostOpacity); + + context.translate(-gx, -gy); } - return updatedEles; -}; + drawLine(); + drawArrows(); + drawOverlay(); + drawText(); -// diffProps : { name => { prev, next } } -styfn.updateTransitions = function (ele, diffProps, isBypass) { - var self = this; - var _p = ele._private; - var props = ele.pstyle('transition-property').value; - var duration = ele.pstyle('transition-duration').pfValue; - var delay = ele.pstyle('transition-delay').pfValue; + if (shiftToOriginWithBb) { + context.translate(bb.x1, bb.y1); + } +}; - if (props.length > 0 && duration > 0) { +CRp.drawEdgePath = function (edge, context, pts, type) { + var rs = edge._private.rscratch; + var canvasCxt = context; + var path = void 0; + var pathCacheHit = false; + var usePaths = this.usePaths(); - var style = {}; + if (usePaths) { + var pathCacheKey = pts.join('$'); + var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey; - // build up the style to animate towards - var anyPrev = false; - for (var i = 0; i < props.length; i++) { - var prop = props[i]; - var styProp = ele.pstyle(prop); - var diffProp = diffProps[prop]; + if (keyMatches) { + path = context = rs.pathCache; + pathCacheHit = true; + } else { + path = context = new Path2D(); // eslint-disable-line no-undef + rs.pathCacheKey = pathCacheKey; + rs.pathCache = path; + } + } - if (!diffProp) { - continue; - } + if (canvasCxt.setLineDash) { + // for very outofdate browsers + switch (type) { + case 'dotted': + canvasCxt.setLineDash([1, 1]); + break; - var prevProp = diffProp.prev; - var fromProp = prevProp; - var toProp = diffProp.next != null ? diffProp.next : styProp; - var diff = false; - var initVal = void 0; - var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity) + case 'dashed': + canvasCxt.setLineDash([6, 3]); + break; - if (!fromProp) { - continue; - } + case 'solid': + canvasCxt.setLineDash([]); + break; + } + } - // consider px values - if (is.number(fromProp.pfValue) && is.number(toProp.pfValue)) { - diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy - initVal = fromProp.pfValue + initDt * diff; + if (!pathCacheHit && !rs.badLine) { + if (context.beginPath) { + context.beginPath(); + } + context.moveTo(pts[0], pts[1]); - // consider numerical values - } else if (is.number(fromProp.value) && is.number(toProp.value)) { - diff = toProp.value - fromProp.value; // nonzero is truthy - initVal = fromProp.value + initDt * diff; + switch (rs.edgeType) { + case 'bezier': + case 'self': + case 'compound': + case 'multibezier': + for (var i = 2; i + 3 < pts.length; i += 4) { + context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]); + } + break; - // consider colour values - } else if (is.array(fromProp.value) && is.array(toProp.value)) { - diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2]; + case 'straight': + case 'segments': + case 'haystack': + for (var _i = 2; _i + 1 < pts.length; _i += 2) { + context.lineTo(pts[_i], pts[_i + 1]); + } + break; + } + } - initVal = fromProp.strValue; - } + context = canvasCxt; + if (usePaths) { + context.stroke(path); + } else { + context.stroke(); + } - // the previous value is good for an animation only if it's different - if (diff) { - style[prop] = toProp.strValue; // to val - this.applyBypass(ele, prop, initVal); // from val - anyPrev = true; - } - } // end if props allow ani + // reset any line dashes + if (context.setLineDash) { + // for very outofdate browsers + context.setLineDash([]); + } +}; - // can't transition if there's nothing previous to transition from - if (!anyPrev) { - return; - } +CRp.drawArrowheads = function (context, edge, opacity) { + var rs = edge._private.rscratch; + var isHaystack = rs.edgeType === 'haystack'; - _p.transitioning = true; + if (!isHaystack) { + this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity); + } - new Promise(function (resolve) { - if (delay > 0) { - ele.delayAnimation(delay).play().promise().then(resolve); - } else { - resolve(); - } - }).then(function () { - return ele.animation({ - style: style, - duration: duration, - easing: ele.pstyle('transition-timing-function').value, - queue: false - }).play().promise(); - }).then(function () { - // if( !isBypass ){ - self.removeBypasses(ele, props); - ele.emitAndNotify('style'); - // } + this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity); - _p.transitioning = false; - }); - } else if (_p.transitioning) { - this.removeBypasses(ele, props); - ele.emitAndNotify('style'); + this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity); - _p.transitioning = false; + if (!isHaystack) { + this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity); } }; -styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) { - var prop = this.properties[name]; +CRp.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) { + if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) { + return; + } - if (prop.triggersZOrder != null && (fromValue == null || prop.triggersZOrder(fromValue, toValue))) { - this._private.cy.notify({ - type: 'zorder', - eles: ele - }); + var self = this; + var arrowShape = edge.pstyle(prefix + '-arrow-shape').value; + if (arrowShape === 'none') { + return; } -}; -module.exports = styfn; + var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled'; + var arrowFill = edge.pstyle(prefix + '-arrow-fill').value; + var edgeWidth = edge.pstyle('width').pfValue; + var edgeOpacity = edge.pstyle('opacity').value; -/***/ }), -/* 123 */ -/***/ (function(module, exports, __webpack_require__) { + if (opacity === undefined) { + opacity = edgeOpacity; + } -"use strict"; + var gco = context.globalCompositeOperation; + if (opacity !== 1 || arrowFill === 'hollow') { + // then extra clear is needed + context.globalCompositeOperation = 'destination-out'; -var is = __webpack_require__(0); -var util = __webpack_require__(1); + self.fillStyle(context, 255, 255, 255, 1); + self.strokeStyle(context, 255, 255, 255, 1); -var styfn = {}; + self.drawArrowShape(edge, prefix, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle); -// bypasses are applied to an existing style on an element, and just tacked on temporarily -// returns true iff application was successful for at least 1 specified property -styfn.applyBypass = function (eles, name, value, updateTransitions) { - var self = this; - var props = []; - var isBypass = true; + context.globalCompositeOperation = gco; + } // otherwise, the opaque arrow clears it for free :) - // put all the properties (can specify one or many) in an array after parsing them - if (name === '*' || name === '**') { - // apply to all property names + var color = edge.pstyle(prefix + '-arrow-color').value; + self.fillStyle(context, color[0], color[1], color[2], opacity); + self.strokeStyle(context, color[0], color[1], color[2], opacity); - if (value !== undefined) { - for (var i = 0; i < self.properties.length; i++) { - var prop = self.properties[i]; - var _name = prop.name; + self.drawArrowShape(edge, prefix, context, arrowFill, edgeWidth, arrowShape, x, y, angle); +}; - var parsedProp = this.parse(_name, value, true); +CRp.drawArrowShape = function (edge, arrowType, context, fill, edgeWidth, shape, x, y, angle) { + var r = this; + var usePaths = this.usePaths(); + var rs = edge._private.rscratch; + var pathCacheHit = false; + var path = void 0; + var canvasContext = context; + var translation = { x: x, y: y }; + var scale = edge.pstyle('arrow-scale').value; + var size = this.getArrowWidth(edgeWidth, scale); + var shapeImpl = r.arrowShapes[shape]; - if (parsedProp) { - props.push(parsedProp); - } - } - } - } else if (is.string(name)) { - // then parse the single property - var _parsedProp = this.parse(name, value, true); + if (usePaths) { + var pathCacheKey = size + '$' + shape + '$' + angle + '$' + x + '$' + y; + rs.arrowPathCacheKey = rs.arrowPathCacheKey || {}; + rs.arrowPathCache = rs.arrowPathCache || {}; - if (_parsedProp) { - props.push(_parsedProp); + var alreadyCached = rs.arrowPathCacheKey[arrowType] === pathCacheKey; + if (alreadyCached) { + path = context = rs.arrowPathCache[arrowType]; + pathCacheHit = true; + } else { + path = context = new Path2D(); // eslint-disable-line no-undef + rs.arrowPathCacheKey[arrowType] = pathCacheKey; + rs.arrowPathCache[arrowType] = path; } - } else if (is.plainObject(name)) { - // then parse each property - var specifiedProps = name; - updateTransitions = value; + } - var names = Object.keys(specifiedProps); + if (context.beginPath) { + context.beginPath(); + } - for (var _i = 0; _i < names.length; _i++) { - var _name2 = names[_i]; - var _prop = self.properties[_name2]; - var _value = specifiedProps[_name2]; + if (!pathCacheHit) { + shapeImpl.draw(context, size, angle, translation, edgeWidth); + } - if (_value === undefined) { - // try camel case name too - _value = specifiedProps[util.dash2camel(_name2)]; - } + if (!shapeImpl.leavePathOpen && context.closePath) { + context.closePath(); + } - if (_value !== undefined) { - var _parsedProp2 = this.parse(_name2, _value, true); + context = canvasContext; - if (_parsedProp2) { - props.push(_parsedProp2); - } - } + if (fill === 'filled' || fill === 'both') { + if (usePaths) { + context.fill(path); + } else { + context.fill(); } - } else { - // can't do anything without well defined properties - return false; } - // we've failed if there are no valid properties - if (props.length === 0) { - return false; + if (fill === 'hollow' || fill === 'both') { + context.lineWidth = shapeImpl.matchEdgeWidth ? edgeWidth : 1; + context.lineJoin = 'miter'; + + if (usePaths) { + context.stroke(path); + } else { + context.stroke(); + } } +}; - // now, apply the bypass properties on the elements - var ret = false; // return true if at least one succesful bypass applied - for (var _i2 = 0; _i2 < eles.length; _i2++) { - // for each ele - var ele = eles[_i2]; - var diffProps = {}; - var diffProp = void 0; +module.exports = CRp; - for (var j = 0; j < props.length; j++) { - // for each prop - var _prop2 = props[j]; +/***/ }), +/* 129 */ +/***/ (function(module, exports, __webpack_require__) { - if (updateTransitions) { - var prevProp = ele.pstyle(_prop2.name); - diffProp = diffProps[_prop2.name] = { prev: prevProp }; - } +"use strict"; - ret = this.applyParsedProperty(ele, _prop2) || ret; - if (updateTransitions) { - diffProp.next = ele.pstyle(_prop2.name); - } - } // for props +var CRp = {}; - if (ret) { - this.updateStyleHints(ele); - } +CRp.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) { + var r = this; - if (updateTransitions) { - this.updateTransitions(ele, diffProps, isBypass); - } - } // for eles + // detect problematic cases for old browsers with bad images (cheaper than try-catch) + if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) { + return; + } - return ret; + context.drawImage(img, ix, iy, iw, ih, x, y, w, h); }; -// only useful in specific cases like animation -styfn.overrideBypass = function (eles, name, value) { - name = util.camel2dash(name); +CRp.drawInscribedImage = function (context, img, node, index, nodeOpacity) { + var r = this; + var pos = node.position(); + var nodeX = pos.x; + var nodeY = pos.y; + var styleObj = node.cy().style(); + var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj); + var fit = getIndexedStyle(node, 'background-fit', 'value', index); + var repeat = getIndexedStyle(node, 'background-repeat', 'value', index); + var nodeW = node.width(); + var nodeH = node.height(); + var paddingX2 = node.padding() * 2; + var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2); + var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2); + var rs = node._private.rscratch; + var clip = node.pstyle('background-clip').value; + var shouldClip = clip === 'node'; + var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity; + + var imgW = img.width || img.cachedW; + var imgH = img.height || img.cachedH; - for (var i = 0; i < eles.length; i++) { - var ele = eles[i]; - var prop = ele._private.style[name]; - var type = this.properties[name].type; - var isColor = type.color; - var isMulti = type.mutiple; + // workaround for broken browsers like ie + if (null == imgW || null == imgH) { + document.body.appendChild(img); // eslint-disable-line no-undef - if (!prop || !prop.bypass) { - // need a bypass if one doesn't exist - this.applyBypass(ele, name, value); - continue; - } + imgW = img.cachedW = img.width || img.offsetWidth; + imgH = img.cachedH = img.height || img.offsetHeight; - var oldValue = prop.pfValue != null ? prop.pfValue : prop.value; + document.body.removeChild(img); // eslint-disable-line no-undef + } - prop.value = value; + var w = imgW; + var h = imgH; - if (prop.pfValue != null) { - prop.pfValue = value; + if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') { + if (getIndexedStyle(node, 'background-width', 'units', index) === '%') { + w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW; + } else { + w = getIndexedStyle(node, 'background-width', 'pfValue', index); } + } - if (isColor) { - prop.strValue = 'rgb(' + value.join(',') + ')'; - } else if (isMulti) { - prop.strValue = value.join(' '); + if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') { + if (getIndexedStyle(node, 'background-height', 'units', index) === '%') { + h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH; } else { - prop.strValue = '' + value; + h = getIndexedStyle(node, 'background-height', 'pfValue', index); } - - this.checkZOrderTrigger(ele, name, oldValue, value); } -}; - -styfn.removeAllBypasses = function (eles, updateTransitions) { - return this.removeBypasses(eles, this.propertyNames, updateTransitions); -}; - -styfn.removeBypasses = function (eles, props, updateTransitions) { - var isBypass = true; - for (var j = 0; j < eles.length; j++) { - var ele = eles[j]; - var diffProps = {}; + if (w === 0 || h === 0) { + return; // no point in drawing empty image (and chrome is broken in this case) + } - for (var i = 0; i < props.length; i++) { - var name = props[i]; - var prop = this.properties[name]; - var prevProp = ele.pstyle(prop.name); + if (fit === 'contain') { + var scale = Math.min(nodeTW / w, nodeTH / h); - if (!prevProp || !prevProp.bypass) { - // if a bypass doesn't exist for the prop, nothing needs to be removed - continue; - } + w *= scale; + h *= scale; + } else if (fit === 'cover') { + var scale = Math.max(nodeTW / w, nodeTH / h); - var value = ''; // empty => remove bypass - var parsedProp = this.parse(name, value, true); - var diffProp = diffProps[prop.name] = { prev: prevProp }; + w *= scale; + h *= scale; + } - this.applyParsedProperty(ele, parsedProp); + var x = nodeX - nodeTW / 2; // left + if (getIndexedStyle(node, 'background-position-x', 'units', index) === '%') { + x += (nodeTW - w) * getIndexedStyle(node, 'background-position-x', 'pfValue', index); + } else { + x += getIndexedStyle(node, 'background-position-x', 'pfValue', index); + } - diffProp.next = ele.pstyle(prop.name); - } // for props + var y = nodeY - nodeTH / 2; // top + if (getIndexedStyle(node, 'background-position-y', 'units', index) === '%') { + y += (nodeTH - h) * getIndexedStyle(node, 'background-position-y', 'pfValue', index); + } else { + y += getIndexedStyle(node, 'background-position-y', 'pfValue', index); + } - this.updateStyleHints(ele); + if (rs.pathCache) { + x -= nodeX; + y -= nodeY; - if (updateTransitions) { - this.updateTransitions(ele, diffProps, isBypass); - } - } // for eles -}; + nodeX = 0; + nodeY = 0; + } -module.exports = styfn; + var gAlpha = context.globalAlpha; -/***/ }), -/* 124 */ -/***/ (function(module, exports, __webpack_require__) { + context.globalAlpha = imgOpacity; -"use strict"; + if (repeat === 'no-repeat') { + if (shouldClip) { + context.save(); -var window = __webpack_require__(4); + if (rs.pathCache) { + context.clip(rs.pathCache); + } else { + r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH); -var styfn = {}; + context.clip(); + } + } -// gets what an em size corresponds to in pixels relative to a dom element -styfn.getEmSizeInPixels = function () { - var px = this.containerCss('font-size'); + r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h); - if (px != null) { - return parseFloat(px); + if (shouldClip) { + context.restore(); + } } else { - return 1; // for headless - } -}; + var pattern = context.createPattern(img, repeat); + context.fillStyle = pattern; -// gets css property from the core container -styfn.containerCss = function (propName) { - var cy = this._private.cy; - var domElement = cy.container(); + r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH); - if (window && domElement && window.getComputedStyle) { - return window.getComputedStyle(domElement).getPropertyValue(propName); + context.translate(x, y); + context.fill(); + context.translate(-x, -y); } + + context.globalAlpha = gAlpha; }; -module.exports = styfn; +module.exports = CRp; /***/ }), -/* 125 */ +/* 130 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(1); -var is = __webpack_require__(0); +var math = __webpack_require__(2); -var styfn = {}; +var CRp = {}; -// gets the rendered style for an element -styfn.getRenderedStyle = function (ele, prop) { - if (prop) { - return this.getStylePropertyValue(ele, prop, true); - } else { - return this.getRawStyle(ele, true); +CRp.eleTextBiggerThanMin = function (ele, scale) { + if (!scale) { + var zoom = ele.cy().zoom(); + var pxRatio = this.getPixelRatio(); + var lvl = Math.ceil(math.log2(zoom * pxRatio)); // the effective texture level + + scale = Math.pow(2, lvl); } -}; -// gets the raw style for an element -styfn.getRawStyle = function (ele, isRenderedVal) { - var self = this; + var computedSize = ele.pstyle('font-size').pfValue * scale; + var minSize = ele.pstyle('min-zoomed-font-size').pfValue; - ele = ele[0]; // insure it's an element + if (computedSize < minSize) { + return false; + } - if (ele) { - var rstyle = {}; + return true; +}; - for (var i = 0; i < self.properties.length; i++) { - var prop = self.properties[i]; - var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal); +CRp.drawElementText = function (context, ele, force) { + var r = this; - if (val != null) { - rstyle[prop.name] = val; - rstyle[util.dash2camel(prop.name)] = val; - } + if (force === undefined) { + if (!r.eleTextBiggerThanMin(ele)) { + return; + } + } else { + if (!force) { + return; } - - return rstyle; } -}; -styfn.getIndexedStyle = function (ele, property, subproperty, index) { - var pstyle = ele.pstyle(property)[subproperty][index]; - return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0]; -}; + if (ele.isNode()) { + var label = ele.pstyle('label'); -styfn.getStylePropertyValue = function (ele, propName, isRenderedVal) { - var self = this; + if (!label || !label.value) { + return; + } - ele = ele[0]; // insure it's an element + var textHalign = ele.pstyle('text-halign').strValue; + var textValign = ele.pstyle('text-valign').strValue; - if (ele) { - var prop = self.properties[propName]; + switch (textHalign) { + case 'left': + context.textAlign = 'right'; + break; - if (prop.alias) { - prop = prop.pointsTo; + case 'right': + context.textAlign = 'left'; + break; + + default: + // e.g. center + context.textAlign = 'center'; } - var type = prop.type; - var styleProp = ele.pstyle(prop.name); - var zoom = ele.cy().zoom(); + context.textBaseline = 'bottom'; + } else { + var label = ele.pstyle('label'); + var srcLabel = ele.pstyle('source-label'); + var tgtLabel = ele.pstyle('target-label'); - if (styleProp) { - var units = styleProp.units ? type.implicitUnits || 'px' : null; - var val = units ? [].concat(styleProp.pfValue).map(function (pfValue) { - return pfValue * (isRenderedVal ? zoom : 1) + units; - }).join(' ') : styleProp.strValue; + if ((!label || !label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) { + return; + } + + context.textAlign = 'center'; + context.textBaseline = 'bottom'; + } - return val; - } + r.drawText(context, ele); + + if (ele.isEdge()) { + r.drawText(context, ele, 'source'); + + r.drawText(context, ele, 'target'); } }; -styfn.getAnimationStartStyle = function (ele, aniProps) { - var rstyle = {}; +CRp.drawNodeText = CRp.drawEdgeText = CRp.drawElementText; - for (var i = 0; i < aniProps.length; i++) { - var aniProp = aniProps[i]; - var name = aniProp.name; +CRp.getFontCache = function (context) { + var cache; - var styleProp = ele.pstyle(name); + this.fontCaches = this.fontCaches || []; - if (styleProp !== undefined) { - // then make a prop of it - if (is.plainObject(styleProp)) { - styleProp = this.parse(name, styleProp.strValue); - } else { - styleProp = this.parse(name, styleProp); - } - } + for (var i = 0; i < this.fontCaches.length; i++) { + cache = this.fontCaches[i]; - if (styleProp) { - rstyle[name] = styleProp; + if (cache.context === context) { + return cache; } } - return rstyle; + cache = { + context: context + }; + this.fontCaches.push(cache); + + return cache; }; -styfn.getPropsList = function (propsObj) { - var self = this; - var rstyle = []; - var style = propsObj; - var props = self.properties; +// set up canvas context with font +// returns transformed text string +CRp.setupTextStyle = function (context, ele) { + // Font style + var parentOpacity = ele.effectiveOpacity(); + var labelStyle = ele.pstyle('font-style').strValue; + var labelSize = ele.pstyle('font-size').pfValue + 'px'; + var labelFamily = ele.pstyle('font-family').strValue; + var labelWeight = ele.pstyle('font-weight').strValue; + var opacity = ele.pstyle('text-opacity').value * ele.pstyle('opacity').value * parentOpacity; + var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity; + var color = ele.pstyle('color').value; + var outlineColor = ele.pstyle('text-outline-color').value; - if (style) { - var names = Object.keys(style); + var fontCacheKey = ele._private.fontKey; + var cache = this.getFontCache(context); - for (var i = 0; i < names.length; i++) { - var name = names[i]; - var val = style[name]; - var prop = props[name] || props[util.camel2dash(name)]; - var styleProp = this.parse(prop.name, val); + if (cache.key !== fontCacheKey) { + context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily; - if (styleProp) { - rstyle.push(styleProp); - } - } + cache.key = fontCacheKey; } - return rstyle; -}; + // Calculate text draw position based on text alignment -module.exports = styfn; + // so text outlines aren't jagged + context.lineJoin = 'round'; -/***/ }), -/* 126 */ -/***/ (function(module, exports, __webpack_require__) { + this.fillStyle(context, color[0], color[1], color[2], opacity); -"use strict"; + this.strokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity); +}; +function roundRect(ctx, x, y, width, height, radius) { + var radius = radius || 5; + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + ctx.fill(); +} -var styfn = {}; +// Draw text +CRp.drawText = function (context, ele, prefix) { + var _p = ele._private; + var rscratch = _p.rscratch; + var parentOpacity = ele.effectiveOpacity(); + if (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0) { + return; + } -styfn.appendFromJson = function (json) { - var style = this; + var textX = util.getPrefixedProperty(rscratch, 'labelX', prefix); + var textY = util.getPrefixedProperty(rscratch, 'labelY', prefix); + var text = this.getLabelText(ele, prefix); - for (var i = 0; i < json.length; i++) { - var context = json[i]; - var selector = context.selector; - var props = context.style || context.css; - var names = Object.keys(props); + if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) { + this.setupTextStyle(context, ele); - style.selector(selector); // apply selector + var pdash = prefix ? prefix + '-' : ''; + var textW = util.getPrefixedProperty(rscratch, 'labelWidth', prefix); + var textH = util.getPrefixedProperty(rscratch, 'labelHeight', prefix); + var textAngle = util.getPrefixedProperty(rscratch, 'labelAngle', prefix); + var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue; + var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue; - for (var j = 0; j < names.length; j++) { - var name = names[j]; - var value = props[name]; + var isEdge = ele.isEdge(); + var isNode = ele.isNode(); - style.css(name, value); // apply property + var halign = ele.pstyle('text-halign').value; + var valign = ele.pstyle('text-valign').value; + + if (isEdge) { + halign = 'center'; + valign = 'center'; } - } - return style; -}; + textX += marginX; + textY += marginY; -// accessible cy.style() function -styfn.fromJson = function (json) { - var style = this; + var rotation = ele.pstyle('text-rotation'); + var theta; - style.resetToDefault(); - style.appendFromJson(json); + if (rotation.strValue === 'autorotate') { + theta = isEdge ? textAngle : 0; + } else if (rotation.strValue === 'none') { + theta = 0; + } else { + theta = rotation.pfValue; + } - return style; -}; + if (theta !== 0) { + var orgTextX = textX; + var orgTextY = textY; -// get json from cy.style() api -styfn.json = function () { - var json = []; + context.translate(orgTextX, orgTextY); + context.rotate(theta); - for (var i = this.defaultLength; i < this.length; i++) { - var cxt = this[i]; - var selector = cxt.selector; - var props = cxt.properties; - var css = {}; + textX = 0; + textY = 0; + } - for (var j = 0; j < props.length; j++) { - var prop = props[j]; - css[prop.name] = prop.strValue; + switch (valign) { + case 'top': + break; + case 'center': + textY += textH / 2; + break; + case 'bottom': + textY += textH; + break; } - json.push({ - selector: !selector ? 'core' : selector.toString(), - style: css - }); - } + var backgroundOpacity = ele.pstyle('text-background-opacity').value; + var borderOpacity = ele.pstyle('text-border-opacity').value; + var textBorderWidth = ele.pstyle('text-border-width').pfValue; + var backgroundPadding = ele.pstyle('text-background-padding').pfValue; + + if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) { + var bgX = textX - backgroundPadding; + + switch (halign) { + case 'left': + bgX -= textW; + break; + case 'center': + bgX -= textW / 2; + break; + case 'right': + break; + } + + var bgY = textY - textH - backgroundPadding; + var bgW = textW + 2 * backgroundPadding; + var bgH = textH + 2 * backgroundPadding; + + if (backgroundOpacity > 0) { + var textFill = context.fillStyle; + var textBackgroundColor = ele.pstyle('text-background-color').value; + + context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')'; + var styleShape = ele.pstyle('text-background-shape').strValue; + if (styleShape == 'roundrectangle') { + roundRect(context, bgX, bgY, bgW, bgH, 2); + } else { + context.fillRect(bgX, bgY, bgW, bgH); + } + context.fillStyle = textFill; + } + + if (textBorderWidth > 0 && borderOpacity > 0) { + var textStroke = context.strokeStyle; + var textLineWidth = context.lineWidth; + var textBorderColor = ele.pstyle('text-border-color').value; + var textBorderStyle = ele.pstyle('text-border-style').value; + + context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')'; + context.lineWidth = textBorderWidth; + + if (context.setLineDash) { + // for very outofdate browsers + switch (textBorderStyle) { + case 'dotted': + context.setLineDash([1, 1]); + break; + case 'dashed': + context.setLineDash([4, 2]); + break; + case 'double': + context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders + context.setLineDash([]); + break; + case 'solid': + context.setLineDash([]); + break; + } + } - return json; -}; + context.strokeRect(bgX, bgY, bgW, bgH); -module.exports = styfn; + if (textBorderStyle === 'double') { + var whiteWidth = textBorderWidth / 2; -/***/ }), -/* 127 */ -/***/ (function(module, exports, __webpack_require__) { + context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2); + } -"use strict"; + if (context.setLineDash) { + // for very outofdate browsers + context.setLineDash([]); + } + context.lineWidth = textLineWidth; + context.strokeStyle = textStroke; + } + } + var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle -var util = __webpack_require__(1); -var is = __webpack_require__(0); -var math = __webpack_require__(2); + if (lineWidth > 0) { + context.lineWidth = lineWidth; + } -var styfn = {}; + if (ele.pstyle('text-wrap').value === 'wrap') { + var lines = util.getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix); + var lineHeight = textH / lines.length; -// a caching layer for property parsing -styfn.parse = function (name, value, propIsBypass, propIsFlat) { - var self = this; + switch (valign) { + case 'top': + textY -= (lines.length - 1) * lineHeight; + break; + case 'center': + case 'bottom': + textY -= (lines.length - 1) * lineHeight; + break; + } - // function values can't be cached in all cases, and there isn't much benefit of caching them anyway - if (is.fn(value)) { - return self.parseImplWarn(name, value, propIsBypass, propIsFlat); - } + for (var l = 0; l < lines.length; l++) { + if (lineWidth > 0) { + context.strokeText(lines[l], textX, textY); + } - var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat; - var argHash = [name, value, propIsBypass, flatKey].join('$'); - var propCache = self.propCache = self.propCache || {}; - var ret = void 0; + context.fillText(lines[l], textX, textY); - if (!(ret = propCache[argHash])) { - ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat); - } + textY += lineHeight; + } + } else { + if (lineWidth > 0) { + context.strokeText(text, textX, textY); + } - // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden - // - mappings can't be shared b/c mappings are per-element - if (propIsBypass || propIsFlat === 'mapping') { - // need a copy since props are mutated later in their lifecycles - ret = util.copy(ret); + context.fillText(text, textX, textY); + } - if (ret) { - ret.value = util.copy(ret.value); // because it could be an array, e.g. colour + if (theta !== 0) { + context.rotate(-theta); + context.translate(-orgTextX, -orgTextY); } } - - return ret; }; -styfn.parseImplWarn = function (name, value, propIsBypass, propIsFlat) { - var prop = this.parseImpl(name, value, propIsBypass, propIsFlat); - - if (!prop && value != null) { - util.error('The style property `%s: %s` is invalid', name, value); - } - - return prop; -}; +module.exports = CRp; -// parse a property; return null on invalid; return parsed property otherwise -// fields : -// - name : the name of the property -// - value : the parsed, native-typed value of the property -// - strValue : a string value that represents the property value in valid css -// - bypass : true iff the property is a bypass property -styfn.parseImpl = function (name, value, propIsBypass, propIsFlat) { - var self = this; +/***/ }), +/* 131 */ +/***/ (function(module, exports, __webpack_require__) { - name = util.camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName') +"use strict"; - var property = self.properties[name]; - var passedValue = value; - var types = self.types; - if (!property) { - return null; - } // return null on property of unknown name - if (value === undefined || value === null) { - return null; - } // can't assign null +/* global Path2D */ - // the property may be an alias - if (property.alias) { - property = property.pointsTo; - name = property.name; - } +var is = __webpack_require__(0); - var valueIsString = is.string(value); - if (valueIsString) { - // trim the value to make parsing easier - value = value.trim(); - } +var CRp = {}; - var type = property.type; - if (!type) { - return null; - } // no type, no luck +CRp.drawNode = function (context, node, shiftToOriginWithBb, drawLabel) { + var r = this; + var nodeWidth = void 0, + nodeHeight = void 0; + var _p = node._private; + var rs = _p.rscratch; + var pos = node.position(); - // check if bypass is null or empty string (i.e. indication to delete bypass property) - if (propIsBypass && (value === '' || value === null)) { - return { - name: name, - value: value, - bypass: true, - deleteBypass: true - }; + if (!is.number(pos.x) || !is.number(pos.y)) { + return; // can't draw node with undefined position } - // check if value is a function used as a mapper - if (is.fn(value)) { - return { - name: name, - value: value, - strValue: 'fn', - mapped: types.fn, - bypass: propIsBypass - }; + if (!node.visible()) { + return; } - // check if value is mapped - var data = void 0, - mapData = void 0; - if (!valueIsString || propIsFlat) { - // then don't bother to do the expensive regex checks - - } else if (data = new RegExp(types.data.regex).exec(value)) { - if (propIsBypass) { - return false; - } // mappers not allowed in bypass - - var mapped = types.data; + var parentOpacity = node.effectiveOpacity(); - return { - name: name, - value: data, - strValue: '' + value, - mapped: mapped, - field: data[1], - bypass: propIsBypass - }; - } else if (mapData = new RegExp(types.mapData.regex).exec(value)) { - if (propIsBypass) { - return false; - } // mappers not allowed in bypass - if (type.multiple) { - return false; - } // impossible to map to num + var usePaths = r.usePaths(); + var path = void 0; + var pathCacheHit = false; - var _mapped = types.mapData; + var padding = node.padding(); - // we can map only if the type is a colour or a number - if (!(type.color || type.number)) { - return false; - } + nodeWidth = node.width() + 2 * padding; + nodeHeight = node.height() + 2 * padding; - var valueMin = this.parse(name, mapData[4]); // parse to validate - if (!valueMin || valueMin.mapped) { - return false; - } // can't be invalid or mapped + // + // setup shift - var valueMax = this.parse(name, mapData[5]); // parse to validate - if (!valueMax || valueMax.mapped) { - return false; - } // can't be invalid or mapped + var bb = void 0; + if (shiftToOriginWithBb) { + bb = shiftToOriginWithBb; - // check if valueMin and valueMax are the same - if (valueMin.value === valueMax.value) { - return false; // can't make much of a mapper without a range - } else if (type.color) { - var c1 = valueMin.value; - var c2 = valueMax.value; + context.translate(-bb.x1, -bb.y1); + } - var same = c1[0] === c2[0] // red - && c1[1] === c2[1] // green - && c1[2] === c2[2] // blue - && ( // optional alpha - c1[3] === c2[3] // same alpha outright - || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1? - c2[3] == null || c2[3] === 1) // full opacity for colour 2? - ); + // + // load bg image - if (same) { - return false; - } // can't make a mapper without a range - } + var bgImgProp = node.pstyle('background-image'); + var urls = bgImgProp.value; + var urlDefined = new Array(urls.length); + var image = new Array(urls.length); + var numImages = 0; + for (var i = 0; i < urls.length; i++) { + var url = urls[i]; + var defd = urlDefined[i] = url != null && url !== 'none'; - return { - name: name, - value: mapData, - strValue: '' + value, - mapped: _mapped, - field: mapData[1], - fieldMin: parseFloat(mapData[2]), // min & max are numeric - fieldMax: parseFloat(mapData[3]), - valueMin: valueMin.value, - valueMax: valueMax.value, - bypass: propIsBypass - }; - } + if (defd) { + var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i); - if (type.multiple && propIsFlat !== 'multiple') { - var vals = void 0; + numImages++; - if (valueIsString) { - vals = value.split(/\s+/); - } else if (is.array(value)) { - vals = value; - } else { - vals = [value]; + // get image, and if not loaded then ask to redraw when later loaded + image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () { + node.emitAndNotify('background'); + }); } + } - if (type.evenMultiple && vals.length % 2 !== 0) { - return null; - } + // + // setup styles - var valArr = []; - var unitsArr = []; - var pfValArr = []; - var hasEnum = false; + var darkness = node.pstyle('background-blacken').value; + var borderWidth = node.pstyle('border-width').pfValue; + var bgColor = node.pstyle('background-color').value; + var bgOpacity = node.pstyle('background-opacity').value * parentOpacity; + var borderColor = node.pstyle('border-color').value; + var borderStyle = node.pstyle('border-style').value; + var borderOpacity = node.pstyle('border-opacity').value * parentOpacity; - for (var i = 0; i < vals.length; i++) { - var p = self.parse(name, vals[i], propIsBypass, 'multiple'); + context.lineJoin = 'miter'; // so borders are square with the node shape - hasEnum = hasEnum || is.string(p.value); + var setupShapeColor = function setupShapeColor() { + var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity; - valArr.push(p.value); - pfValArr.push(p.pfValue != null ? p.pfValue : p.value); - unitsArr.push(p.units); - } + r.fillStyle(context, bgColor[0], bgColor[1], bgColor[2], bgOpy); + }; - if (type.validate && !type.validate(valArr, unitsArr)) { - return null; - } + var setupBorderColor = function setupBorderColor() { + var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity; - if (type.singleEnum && hasEnum) { - if (valArr.length === 1 && is.string(valArr[0])) { - return { - name: name, - value: valArr[0], - strValue: valArr[0], - bypass: propIsBypass - }; - } else { - return null; - } - } + r.strokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy); + }; - return { - name: name, - value: valArr, - pfValue: pfValArr, - strValue: valArr.join(' '), - bypass: propIsBypass, - units: unitsArr - }; + // + // setup shape + + var styleShape = node.pstyle('shape').strValue; + var shapePts = node.pstyle('shape-polygon-points').pfValue; + + if (usePaths) { + var pathCacheKey = styleShape + '$' + nodeWidth + '$' + nodeHeight + (styleShape === 'polygon' ? '$' + shapePts.join('$') : ''); + + context.translate(pos.x, pos.y); + + if (rs.pathCacheKey === pathCacheKey) { + path = rs.pathCache; + pathCacheHit = true; + } else { + path = new Path2D(); + rs.pathCacheKey = pathCacheKey; + rs.pathCache = path; + } } - // several types also allow enums - var checkEnums = function checkEnums() { - for (var _i = 0; _i < type.enums.length; _i++) { - var en = type.enums[_i]; + var drawShape = function drawShape() { + if (!pathCacheHit) { - if (en === value) { - return { - name: name, - value: value, - strValue: '' + value, - bypass: propIsBypass + var npos = pos; + + if (usePaths) { + npos = { + x: 0, + y: 0 }; } + + r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight); } - return null; + if (usePaths) { + context.fill(path); + } else { + context.fill(); + } }; - // check the type and return the appropriate object - if (type.number) { - var units = void 0; - var implicitUnits = 'px'; // not set => px + var drawImages = function drawImages() { + var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : parentOpacity; - if (type.units) { - // use specified units if set - units = type.units; - } + var prevBging = _p.backgrounding; + var totalCompleted = 0; - if (type.implicitUnits) { - implicitUnits = type.implicitUnits; + for (var _i = 0; _i < image.length; _i++) { + if (urlDefined[_i] && image[_i].complete && !image[_i].error) { + totalCompleted++; + r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity); + } } - if (!type.unitless) { - if (valueIsString) { - var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : ''); - if (units) { - unitsRegex = units; - } // only allow explicit units if so set - var match = value.match('^(' + util.regex.number + ')(' + unitsRegex + ')?' + '$'); - - if (match) { - value = match[1]; - units = match[2] || implicitUnits; - } - } else if (!units || type.implicitUnits) { - units = implicitUnits; // implicitly px if unspecified - } + _p.backgrounding = !(totalCompleted === numImages); + if (prevBging !== _p.backgrounding) { + // update style b/c :backgrounding state changed + node.updateStyle(false); } + }; - value = parseFloat(value); + var drawPie = function drawPie() { + var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : parentOpacity; - // if not a number and enums not allowed, then the value is invalid - if (isNaN(value) && type.enums === undefined) { - return null; - } + if (r.hasPie(node)) { + r.drawPie(context, node, pieOpacity); - // check if this number type also accepts special keywords in place of numbers - // (i.e. `left`, `auto`, etc) - if (isNaN(value) && type.enums !== undefined) { - value = passedValue; + // redraw/restore path if steps after pie need it + if (redrawShape) { - return checkEnums(); + if (!usePaths) { + r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight); + } + } } + }; - // check if value must be an integer - if (type.integer && !is.integer(value)) { - return null; - } + var darken = function darken() { + var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : parentOpacity; - // check value is within range - if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) { - return null; - } + var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity; + var c = darkness > 0 ? 0 : 255; - var ret = { - name: name, - value: value, - strValue: '' + value + (units ? units : ''), - units: units, - bypass: propIsBypass - }; + if (darkness !== 0) { + r.fillStyle(context, c, c, c, opacity); - // normalise value in pixels - if (type.unitless || units !== 'px' && units !== 'em') { - ret.pfValue = value; - } else { - ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value; + if (usePaths) { + context.fill(path); + } else { + context.fill(); + } } + }; - // normalise value in ms - if (units === 'ms' || units === 's') { - ret.pfValue = units === 'ms' ? value : 1000 * value; - } + var drawBorder = function drawBorder() { + if (borderWidth > 0) { - // normalise value in rad - if (units === 'deg' || units === 'rad') { - ret.pfValue = units === 'rad' ? value : math.deg2rad(value); - } + context.lineWidth = borderWidth; + context.lineCap = 'butt'; - // normalize value in % - if (units === '%') { - ret.pfValue = value / 100; - } + if (context.setLineDash) { + // for very outofdate browsers + switch (borderStyle) { + case 'dotted': + context.setLineDash([1, 1]); + break; - return ret; - } else if (type.propList) { + case 'dashed': + context.setLineDash([4, 2]); + break; - var props = []; - var propsStr = '' + value; + case 'solid': + case 'double': + context.setLineDash([]); + break; + } + } - if (propsStr === 'none') { - // leave empty + if (usePaths) { + context.stroke(path); + } else { + context.stroke(); + } - } else { - // go over each prop + if (borderStyle === 'double') { + context.lineWidth = borderWidth / 3; - var propsSplit = propsStr.split(','); - for (var _i2 = 0; _i2 < propsSplit.length; _i2++) { - var propName = propsSplit[_i2].trim(); + var gco = context.globalCompositeOperation; + context.globalCompositeOperation = 'destination-out'; - if (self.properties[propName]) { - props.push(propName); + if (usePaths) { + context.stroke(path); + } else { + context.stroke(); } + + context.globalCompositeOperation = gco; } - if (props.length === 0) { - return null; + // reset in case we changed the border style + if (context.setLineDash) { + // for very outofdate browsers + context.setLineDash([]); } } + }; - return { - name: name, - value: props, - strValue: props.length === 0 ? 'none' : props.join(', '), - bypass: propIsBypass - }; - } else if (type.color) { - var tuple = util.color2tuple(value); - - if (!tuple) { - return null; - } + var drawOverlay = function drawOverlay() { + var overlayPadding = node.pstyle('overlay-padding').pfValue; + var overlayOpacity = node.pstyle('overlay-opacity').value; + var overlayColor = node.pstyle('overlay-color').value; - return { - name: name, - value: tuple, - pfValue: tuple, - strValue: '' + value, - bypass: propIsBypass - }; - } else if (type.regex || type.regexes) { + if (overlayOpacity > 0) { + r.fillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity); - // first check enums - if (type.enums) { - var enumProp = checkEnums(); + r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2); - if (enumProp) { - return enumProp; - } + context.fill(); } + }; - var regexes = type.regexes ? type.regexes : [type.regex]; + var drawText = function drawText() { + r.drawElementText(context, node, drawLabel); + }; - for (var _i3 = 0; _i3 < regexes.length; _i3++) { - var regex = new RegExp(regexes[_i3]); // make a regex from the type string - var m = regex.exec(value); + var ghost = node.pstyle('ghost').value === 'yes'; - if (m) { - // regex matches - return { - name: name, - value: type.singleRegexMatchValue ? m[1] : m, - strValue: '' + value, - bypass: propIsBypass - }; - } - } + if (ghost) { + var gx = node.pstyle('ghost-offset-x').pfValue; + var gy = node.pstyle('ghost-offset-y').pfValue; + var ghostOpacity = node.pstyle('ghost-opacity').value; + var effGhostOpacity = ghostOpacity * parentOpacity; - return null; // didn't match any - } else if (type.string) { - // just return - return { - name: name, - value: '' + value, - strValue: '' + value, - bypass: propIsBypass - }; - } else if (type.enums) { - // check enums last because it's a combo type in others - return checkEnums(); - } else { - return null; // not a type we can handle + context.translate(gx, gy); + + setupShapeColor(ghostOpacity * bgOpacity); + drawShape(); + drawImages(effGhostOpacity); + drawPie(darkness !== 0 || borderWidth !== 0); + darken(effGhostOpacity); + setupBorderColor(ghostOpacity * borderOpacity); + drawBorder(); + + context.translate(-gx, -gy); } -}; -module.exports = styfn; + setupShapeColor(); + drawShape(); + drawImages(); + drawPie(darkness !== 0 || borderWidth !== 0); + darken(); + setupBorderColor(); + drawBorder(); -/***/ }), -/* 128 */ -/***/ (function(module, exports, __webpack_require__) { + if (usePaths) { + context.translate(-pos.x, -pos.y); + } -"use strict"; + drawText(); + drawOverlay(); + // + // clean up shift -var util = __webpack_require__(1); -var is = __webpack_require__(0); + if (shiftToOriginWithBb) { + context.translate(bb.x1, bb.y1); + } +}; -var styfn = {}; +// does the node have at least one pie piece? +CRp.hasPie = function (node) { + node = node[0]; // ensure ele ref -(function () { - var number = util.regex.number; - var rgba = util.regex.rgbaNoBackRefs; - var hsla = util.regex.hslaNoBackRefs; - var hex3 = util.regex.hex3; - var hex6 = util.regex.hex6; - var data = function data(prefix) { - return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$'; - }; - var mapData = function mapData(prefix) { - var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3 + '|' + hex6; - return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$'; - }; - var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; + return node._private.hasPie; +}; - // each visual style property has a type and needs to be validated according to it - styfn.types = { - time: { number: true, min: 0, units: 's|ms', implicitUnits: 'ms' }, - percent: { number: true, min: 0, max: 100, units: '%', implicitUnits: '%' }, - zeroOneNumber: { number: true, min: 0, max: 1, unitless: true }, - zeroOneNumbers: { number: true, min: 0, max: 1, unitless: true, multiple: true }, - nOneOneNumber: { number: true, min: -1, max: 1, unitless: true }, - nonNegativeInt: { number: true, min: 0, integer: true, unitless: true }, - position: { enums: ['parent', 'origin'] }, - nodeSize: { number: true, min: 0, enums: ['label'] }, - number: { number: true, unitless: true }, - numbers: { number: true, unitless: true, multiple: true }, - positiveNumber: { number: true, unitless: true, min: 0, strictMin: true }, - size: { number: true, min: 0 }, - bidirectionalSize: { number: true }, // allows negative - bidirectionalSizes: { number: true, multiple: true }, // allows negative - sizeMaybePercent: { number: true, min: 0, allowPercent: true }, - paddingRelativeTo: { enums: ['width', 'height', 'average', 'min', 'max'] }, - bgWH: { number: true, min: 0, allowPercent: true, enums: ['auto'], multiple: true }, - bgPos: { number: true, allowPercent: true, multiple: true }, - bgRelativeTo: { enums: ['inner', 'include-padding'], multiple: true }, - bgRepeat: { enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'], multiple: true }, - bgFit: { enums: ['none', 'contain', 'cover'], multiple: true }, - bgCrossOrigin: { enums: ['anonymous', 'use-credentials'], multiple: true }, - bgClip: { enums: ['none', 'node'] }, - color: { color: true }, - bool: { enums: ['yes', 'no'] }, - lineStyle: { enums: ['solid', 'dotted', 'dashed'] }, - borderStyle: { enums: ['solid', 'dotted', 'dashed', 'double'] }, - curveStyle: { enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments'] }, - fontFamily: { regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$' }, - fontletiant: { enums: ['small-caps', 'normal'] }, - fontStyle: { enums: ['italic', 'normal', 'oblique'] }, - fontWeight: { enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900] }, - textDecoration: { enums: ['none', 'underline', 'overline', 'line-through'] }, - textTransform: { enums: ['none', 'uppercase', 'lowercase'] }, - textWrap: { enums: ['none', 'wrap', 'ellipsis'] }, - textBackgroundShape: { enums: ['rectangle', 'roundrectangle'] }, - nodeShape: { enums: ['rectangle', 'roundrectangle', 'cutrectangle', 'bottomroundrectangle', 'barrel', 'ellipse', 'triangle', 'square', 'pentagon', 'hexagon', 'concavehexagon', 'heptagon', 'octagon', 'tag', 'star', 'diamond', 'vee', 'rhomboid', 'polygon'] }, - compoundIncludeLabels: { enums: ['include', 'exclude'] }, - arrowShape: { enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'half-triangle-overshot', 'vee', 'square', 'circle', 'diamond', 'none'] }, - arrowFill: { enums: ['filled', 'hollow'] }, - display: { enums: ['element', 'none'] }, - visibility: { enums: ['hidden', 'visible'] }, - zCompoundDepth: { enums: ['bottom', 'orphan', 'auto', 'top'] }, - zIndexCompare: { enums: ['auto', 'manual'] }, - valign: { enums: ['top', 'center', 'bottom'] }, - halign: { enums: ['left', 'center', 'right'] }, - text: { string: true }, - data: { mapping: true, regex: data('data') }, - layoutData: { mapping: true, regex: data('layoutData') }, - scratch: { mapping: true, regex: data('scratch') }, - mapData: { mapping: true, regex: mapData('mapData') }, - mapLayoutData: { mapping: true, regex: mapData('mapLayoutData') }, - mapScratch: { mapping: true, regex: mapData('mapScratch') }, - fn: { mapping: true, fn: true }, - url: { regexes: urlRegexes, singleRegexMatchValue: true }, - urls: { regexes: urlRegexes, singleRegexMatchValue: true, multiple: true }, - propList: { propList: true }, - angle: { number: true, units: 'deg|rad', implicitUnits: 'rad' }, - textRotation: { number: true, units: 'deg|rad', implicitUnits: 'rad', enums: ['none', 'autorotate'] }, - polygonPointList: { number: true, multiple: true, evenMultiple: true, min: -1, max: 1, unitless: true }, - edgeDistances: { enums: ['intersection', 'node-position'] }, - edgeEndpoint: { - number: true, multiple: true, units: '%|px|em|deg|rad', implicitUnits: 'px', - enums: ['inside-to-node', 'outside-to-node', 'outside-to-line'], singleEnum: true, - validate: function validate(valArr, unitsArr) { - switch (valArr.length) { - case 2: - // can be % or px only - return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad'; - case 1: - // can be enum, deg, or rad only - return is.string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad'; - default: - return false; - } - } - }, - easing: { - regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'], - enums: ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'ease-in-sine', 'ease-out-sine', 'ease-in-out-sine', 'ease-in-quad', 'ease-out-quad', 'ease-in-out-quad', 'ease-in-cubic', 'ease-out-cubic', 'ease-in-out-cubic', 'ease-in-quart', 'ease-out-quart', 'ease-in-out-quart', 'ease-in-quint', 'ease-out-quint', 'ease-in-out-quint', 'ease-in-expo', 'ease-out-expo', 'ease-in-out-expo', 'ease-in-circ', 'ease-out-circ', 'ease-in-out-circ'] - } - }; +CRp.drawPie = function (context, node, nodeOpacity, pos) { + node = node[0]; // ensure ele ref + pos = pos || node.position(); - var zOrderDiff = { - zeroNonZero: function zeroNonZero(val1, val2) { - if (val1 === 0 && val2 !== 0) { - return true; - } else if (val1 !== 0 && val2 === 0) { - return true; - } else { - return false; - } - }, - anyDiff: function anyDiff(val1, val2) { - return val1 !== val2; - } - }; + var cyStyle = node.cy().style(); + var pieSize = node.pstyle('pie-size'); + var x = pos.x; + var y = pos.y; + var nodeW = node.width(); + var nodeH = node.height(); + var radius = Math.min(nodeW, nodeH) / 2; // must fit in node + var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1] + var usePaths = this.usePaths(); - var zd = zOrderDiff; + if (usePaths) { + x = 0; + y = 0; + } - // define visual style properties - var t = styfn.types; - var props = styfn.properties = [ - // main label - { name: 'label', type: t.text }, { name: 'text-rotation', type: t.textRotation }, { name: 'text-margin-x', type: t.bidirectionalSize }, { name: 'text-margin-y', type: t.bidirectionalSize }, + if (pieSize.units === '%') { + radius = radius * pieSize.pfValue; + } else if (pieSize.pfValue !== undefined) { + radius = pieSize.pfValue / 2; + } - // source label - { name: 'source-label', type: t.text }, { name: 'source-text-rotation', type: t.textRotation }, { name: 'source-text-margin-x', type: t.bidirectionalSize }, { name: 'source-text-margin-y', type: t.bidirectionalSize }, { name: 'source-text-offset', type: t.size }, + for (var i = 1; i <= cyStyle.pieBackgroundN; i++) { + // 1..N + var size = node.pstyle('pie-' + i + '-background-size').value; + var color = node.pstyle('pie-' + i + '-background-color').value; + var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity; + var percent = size / 100; // map integer range [0, 100] to [0, 1] - // target label - { name: 'target-label', type: t.text }, { name: 'target-text-rotation', type: t.textRotation }, { name: 'target-text-margin-x', type: t.bidirectionalSize }, { name: 'target-text-margin-y', type: t.bidirectionalSize }, { name: 'target-text-offset', type: t.size }, + // percent can't push beyond 1 + if (percent + lastPercent > 1) { + percent = 1 - lastPercent; + } - // common label style - { name: 'text-valign', type: t.valign }, { name: 'text-halign', type: t.halign }, { name: 'color', type: t.color }, { name: 'text-outline-color', type: t.color }, { name: 'text-outline-width', type: t.size }, { name: 'text-outline-opacity', type: t.zeroOneNumber }, { name: 'text-opacity', type: t.zeroOneNumber }, { name: 'text-background-color', type: t.color }, { name: 'text-background-opacity', type: t.zeroOneNumber }, { name: 'text-background-padding', type: t.size }, { name: 'text-border-opacity', type: t.zeroOneNumber }, { name: 'text-border-color', type: t.color }, { name: 'text-border-width', type: t.size }, { name: 'text-border-style', type: t.borderStyle }, { name: 'text-background-shape', type: t.textBackgroundShape }, - // { name: 'text-decoration', type: t.textDecoration }, // not supported in canvas - { name: 'text-transform', type: t.textTransform }, { name: 'text-wrap', type: t.textWrap }, { name: 'text-max-width', type: t.size }, { name: 'text-events', type: t.bool }, { name: 'font-family', type: t.fontFamily }, { name: 'font-style', type: t.fontStyle }, - // { name: 'font-letiant', type: t.fontletiant }, // not useful - { name: 'font-weight', type: t.fontWeight }, { name: 'font-size', type: t.size }, { name: 'min-zoomed-font-size', type: t.size }, + var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise + var angleDelta = 2 * Math.PI * percent; + var angleEnd = angleStart + angleDelta; - // behaviour - { name: 'events', type: t.bool }, + // ignore if + // - zero size + // - we're already beyond the full circle + // - adding the current slice would go beyond the full circle + if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) { + continue; + } - // visibility - { name: 'display', type: t.display, triggersZOrder: zd.anyDiff }, { name: 'visibility', type: t.visibility, triggersZOrder: zd.anyDiff }, { name: 'opacity', type: t.zeroOneNumber, triggersZOrder: zd.zeroNonZero }, { name: 'z-compound-depth', type: t.zCompoundDepth, triggersZOrder: zd.anyDiff }, { name: 'z-index-compare', type: t.zIndexCompare, triggersZOrder: zd.anyDiff }, { name: 'z-index', type: t.nonNegativeInt, triggersZOrder: zd.anyDiff }, + context.beginPath(); + context.moveTo(x, y); + context.arc(x, y, radius, angleStart, angleEnd); + context.closePath(); - // overlays - { name: 'overlay-padding', type: t.size }, { name: 'overlay-color', type: t.color }, { name: 'overlay-opacity', type: t.zeroOneNumber }, + this.fillStyle(context, color[0], color[1], color[2], opacity); - // transition anis - { name: 'transition-property', type: t.propList }, { name: 'transition-duration', type: t.time }, { name: 'transition-delay', type: t.time }, { name: 'transition-timing-function', type: t.easing }, + context.fill(); - // node body - { name: 'height', type: t.nodeSize }, { name: 'width', type: t.nodeSize }, { name: 'shape', type: t.nodeShape }, { name: 'shape-polygon-points', type: t.polygonPointList }, { name: 'background-color', type: t.color }, { name: 'background-opacity', type: t.zeroOneNumber }, { name: 'background-blacken', type: t.nOneOneNumber }, { name: 'padding', type: t.sizeMaybePercent }, { name: 'padding-relative-to', type: t.paddingRelativeTo }, + lastPercent += percent; + } +}; - // node border - { name: 'border-color', type: t.color }, { name: 'border-opacity', type: t.zeroOneNumber }, { name: 'border-width', type: t.size }, { name: 'border-style', type: t.borderStyle }, +module.exports = CRp; - // node background images - { name: 'background-image', type: t.urls }, { name: 'background-image-crossorigin', type: t.bgCrossOrigin }, { name: 'background-image-opacity', type: t.zeroOneNumbers }, { name: 'background-position-x', type: t.bgPos }, { name: 'background-position-y', type: t.bgPos }, { name: 'background-width-relative-to', type: t.bgRelativeTo }, { name: 'background-height-relative-to', type: t.bgRelativeTo }, { name: 'background-repeat', type: t.bgRepeat }, { name: 'background-fit', type: t.bgFit }, { name: 'background-clip', type: t.bgClip }, { name: 'background-width', type: t.bgWH }, { name: 'background-height', type: t.bgWH }, +/***/ }), +/* 132 */ +/***/ (function(module, exports, __webpack_require__) { - // compound props - { name: 'position', type: t.position }, { name: 'compound-sizing-wrt-labels', type: t.compoundIncludeLabels }, { name: 'min-width', type: t.size }, { name: 'min-width-bias-left', type: t.sizeMaybePercent }, { name: 'min-width-bias-right', type: t.sizeMaybePercent }, { name: 'min-height', type: t.size }, { name: 'min-height-bias-top', type: t.sizeMaybePercent }, { name: 'min-height-bias-bottom', type: t.sizeMaybePercent }, +"use strict"; - // edge line - { name: 'line-style', type: t.lineStyle }, { name: 'line-color', type: t.color }, { name: 'curve-style', type: t.curveStyle }, { name: 'haystack-radius', type: t.zeroOneNumber }, { name: 'source-endpoint', type: t.edgeEndpoint }, { name: 'target-endpoint', type: t.edgeEndpoint }, { name: 'control-point-step-size', type: t.size }, { name: 'control-point-distances', type: t.bidirectionalSizes }, { name: 'control-point-weights', type: t.numbers }, { name: 'segment-distances', type: t.bidirectionalSizes }, { name: 'segment-weights', type: t.numbers }, { name: 'edge-distances', type: t.edgeDistances }, { name: 'arrow-scale', type: t.positiveNumber }, { name: 'loop-direction', type: t.angle }, { name: 'loop-sweep', type: t.angle }, { name: 'source-distance-from-node', type: t.size }, { name: 'target-distance-from-node', type: t.size }, - // ghost properties - { name: 'ghost', type: t.bool }, { name: 'ghost-offset-x', type: t.bidirectionalSize }, { name: 'ghost-offset-y', type: t.bidirectionalSize }, { name: 'ghost-opacity', type: t.zeroOneNumber }, +var CRp = {}; - // these are just for the core - { name: 'selection-box-color', type: t.color }, { name: 'selection-box-opacity', type: t.zeroOneNumber }, { name: 'selection-box-border-color', type: t.color }, { name: 'selection-box-border-width', type: t.size }, { name: 'active-bg-color', type: t.color }, { name: 'active-bg-opacity', type: t.zeroOneNumber }, { name: 'active-bg-size', type: t.size }, { name: 'outside-texture-bg-color', type: t.color }, { name: 'outside-texture-bg-opacity', type: t.zeroOneNumber }]; +var util = __webpack_require__(1); - // define aliases - var aliases = styfn.aliases = [{ name: 'content', pointsTo: 'label' }, { name: 'control-point-distance', pointsTo: 'control-point-distances' }, { name: 'control-point-weight', pointsTo: 'control-point-weights' }, { name: 'edge-text-rotation', pointsTo: 'text-rotation' }, { name: 'padding-left', pointsTo: 'padding' }, { name: 'padding-right', pointsTo: 'padding' }, { name: 'padding-top', pointsTo: 'padding' }, { name: 'padding-bottom', pointsTo: 'padding' }]; +var motionBlurDelay = 100; - // pie backgrounds for nodes - styfn.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use) - props.push({ name: 'pie-size', type: t.sizeMaybePercent }); - for (var i = 1; i <= styfn.pieBackgroundN; i++) { - props.push({ name: 'pie-' + i + '-background-color', type: t.color }); - props.push({ name: 'pie-' + i + '-background-size', type: t.percent }); - props.push({ name: 'pie-' + i + '-background-opacity', type: t.zeroOneNumber }); +// var isFirefox = typeof InstallTrigger !== 'undefined'; + +CRp.getPixelRatio = function () { + var context = this.data.contexts[0]; + + if (this.forcedPixelRatio != null) { + return this.forcedPixelRatio; } - // edge arrows - var arrowPrefixes = styfn.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target']; - [{ name: 'arrow-shape', type: t.arrowShape }, { name: 'arrow-color', type: t.color }, { name: 'arrow-fill', type: t.arrowFill }].forEach(function (prop) { - arrowPrefixes.forEach(function (prefix) { - var name = prefix + '-' + prop.name; - var type = prop.type; + var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; - props.push({ name: name, type: type }); - }); - }, {}); + return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef +}; - // list of property names - styfn.propertyNames = props.map(function (p) { - return p.name; - }); +CRp.paintCache = function (context) { + var caches = this.paintCaches = this.paintCaches || []; + var needToCreateCache = true; + var cache; - // allow access of properties by name ( e.g. style.properties.height ) - for (var _i = 0; _i < props.length; _i++) { - var prop = props[_i]; + for (var i = 0; i < caches.length; i++) { + cache = caches[i]; - props[prop.name] = prop; // allow lookup by name + if (cache.context === context) { + needToCreateCache = false; + break; + } } - // map aliases - for (var _i2 = 0; _i2 < aliases.length; _i2++) { - var alias = aliases[_i2]; - var pointsToProp = props[alias.pointsTo]; - var aliasProp = { - name: alias.name, - alias: true, - pointsTo: pointsToProp + if (needToCreateCache) { + cache = { + context: context }; + caches.push(cache); + } - // add alias prop for parsing - props.push(aliasProp); + return cache; +}; - props[alias.name] = aliasProp; // allow lookup by name - } -})(); +CRp.fillStyle = function (context, r, g, b, a) { + context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; -styfn.getDefaultProperty = function (name) { - return this.getDefaultProperties()[name]; + // turn off for now, seems context does its own caching + + // var cache = this.paintCache(context); + + // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + + // if( cache.fillStyle !== fillStyle ){ + // context.fillStyle = cache.fillStyle = fillStyle; + // } }; -styfn.getDefaultProperties = util.memoize(function () { - var rawProps = util.extend({ - // common node/edge props - 'events': 'yes', - 'text-events': 'no', - 'text-valign': 'top', - 'text-halign': 'center', - 'color': '#000', - 'text-outline-color': '#000', - 'text-outline-width': 0, - 'text-outline-opacity': 1, - 'text-opacity': 1, - 'text-decoration': 'none', - 'text-transform': 'none', - 'text-wrap': 'none', - 'text-max-width': 9999, - 'text-background-color': '#000', - 'text-background-opacity': 0, - 'text-background-shape': 'rectangle', - 'text-background-padding': 0, - 'text-border-opacity': 0, - 'text-border-width': 0, - 'text-border-style': 'solid', - 'text-border-color': '#000', - 'font-family': 'Helvetica Neue, Helvetica, sans-serif', - 'font-style': 'normal', - // 'font-letiant': fontletiant, - 'font-weight': 'normal', - 'font-size': 16, - 'min-zoomed-font-size': 0, - 'text-rotation': 'none', - 'source-text-rotation': 'none', - 'target-text-rotation': 'none', - 'visibility': 'visible', - 'display': 'element', - 'opacity': 1, - 'z-compound-depth': 'auto', - 'z-index-compare': 'auto', - 'z-index': 0, - 'label': '', - 'text-margin-x': 0, - 'text-margin-y': 0, - 'source-label': '', - 'source-text-offset': 0, - 'source-text-margin-x': 0, - 'source-text-margin-y': 0, - 'target-label': '', - 'target-text-offset': 0, - 'target-text-margin-x': 0, - 'target-text-margin-y': 0, - 'overlay-opacity': 0, - 'overlay-color': '#000', - 'overlay-padding': 10, - 'transition-property': 'none', - 'transition-duration': 0, - 'transition-delay': 0, - 'transition-timing-function': 'linear', +CRp.strokeStyle = function (context, r, g, b, a) { + context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; - // node props - 'background-blacken': 0, - 'background-color': '#999', - 'background-opacity': 1, - 'background-image': 'none', - 'background-image-crossorigin': 'anonymous', - 'background-image-opacity': 1, - 'background-position-x': '50%', - 'background-position-y': '50%', - 'background-width-relative-to': 'include-padding', - 'background-height-relative-to': 'include-padding', - 'background-repeat': 'no-repeat', - 'background-fit': 'none', - 'background-clip': 'node', - 'background-width': 'auto', - 'background-height': 'auto', - 'border-color': '#000', - 'border-opacity': 1, - 'border-width': 0, - 'border-style': 'solid', - 'height': 30, - 'width': 30, - 'shape': 'ellipse', - 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1', + // turn off for now, seems context does its own caching + + // var cache = this.paintCache(context); + + // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; + + // if( cache.strokeStyle !== strokeStyle ){ + // context.strokeStyle = cache.strokeStyle = strokeStyle; + // } +}; - // ghost props - 'ghost': 'no', - 'ghost-offset-y': 0, - 'ghost-offset-x': 0, - 'ghost-opacity': 0, +// Resize canvas +CRp.matchCanvasSize = function (container) { + var r = this; + var data = r.data; + var bb = r.findContainerClientCoords(); + var width = bb[2]; + var height = bb[3]; + var pixelRatio = r.getPixelRatio(); + var mbPxRatio = r.motionBlurPxRatio; - // compound props - 'padding': 0, - 'padding-relative-to': 'width', - 'position': 'origin', - 'compound-sizing-wrt-labels': 'include', - 'min-width': 0, - 'min-width-bias-left': 0, - 'min-width-bias-right': 0, - 'min-height': 0, - 'min-height-bias-top': 0, - 'min-height-bias-bottom': 0 - }, { - // node pie bg - 'pie-size': '100%' - }, [{ name: 'pie-{{i}}-background-color', value: 'black' }, { name: 'pie-{{i}}-background-size', value: '0%' }, { name: 'pie-{{i}}-background-opacity', value: 1 }].reduce(function (css, prop) { - for (var i = 1; i <= styfn.pieBackgroundN; i++) { - var name = prop.name.replace('{{i}}', i); - var val = prop.value; + if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) { + pixelRatio = mbPxRatio; + } - css[name] = val; - } + var canvasWidth = width * pixelRatio; + var canvasHeight = height * pixelRatio; + var canvas; - return css; - }, {}), { - // edge props - 'line-style': 'solid', - 'line-color': '#999', - 'control-point-step-size': 40, - 'control-point-weights': 0.5, - 'segment-weights': 0.5, - 'segment-distances': 20, - 'edge-distances': 'intersection', - 'curve-style': 'bezier', - 'haystack-radius': 0, - 'arrow-scale': 1, - 'loop-direction': '-45deg', - 'loop-sweep': '-90deg', - 'source-distance-from-node': 0, - 'target-distance-from-node': 0, - 'source-endpoint': 'outside-to-node', - 'target-endpoint': 'outside-to-node' - }, [{ name: 'arrow-shape', value: 'none' }, { name: 'arrow-color', value: '#999' }, { name: 'arrow-fill', value: 'filled' }].reduce(function (css, prop) { - styfn.arrowPrefixes.forEach(function (prefix) { - var name = prefix + '-' + prop.name; - var val = prop.value; + if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) { + return; // save cycles if same + } - css[name] = val; - }); + r.fontCaches = null; // resizing resets the style - return css; - }, {})); + var canvasContainer = data.canvasContainer; + canvasContainer.style.width = width + 'px'; + canvasContainer.style.height = height + 'px'; - var parsedProps = {}; + for (var i = 0; i < r.CANVAS_LAYERS; i++) { + canvas = data.canvases[i]; - for (var i = 0; i < this.properties.length; i++) { - var prop = this.properties[i]; + canvas.width = canvasWidth; + canvas.height = canvasHeight; - if (prop.pointsTo) { - continue; - } + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + } - var name = prop.name; - var val = rawProps[name]; - var parsedProp = this.parse(name, val); + for (var i = 0; i < r.BUFFER_COUNT; i++) { + canvas = data.bufferCanvases[i]; - parsedProps[name] = parsedProp; + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; } - return parsedProps; -}); + r.textureMult = 1; + if (pixelRatio <= 1) { + canvas = data.bufferCanvases[r.TEXTURE_BUFFER]; -styfn.addDefaultStylesheet = function () { - this.selector('$node > node') // compound (parent) node properties - .css({ - 'shape': 'rectangle', - 'padding': 10, - 'background-color': '#eee', - 'border-color': '#ccc', - 'border-width': 1 - }).selector('edge') // just edge properties - .css({ - 'width': 3, - 'curve-style': 'haystack' - }).selector(':parent <-> node').css({ - 'curve-style': 'bezier', - 'source-endpoint': 'outside-to-line', - 'target-endpoint': 'outside-to-line' - }).selector(':selected').css({ - 'background-color': '#0169D9', - 'line-color': '#0169D9', - 'source-arrow-color': '#0169D9', - 'target-arrow-color': '#0169D9', - 'mid-source-arrow-color': '#0169D9', - 'mid-target-arrow-color': '#0169D9' - }).selector('node:parent:selected').css({ - 'background-color': '#CCE1F9', - 'border-color': '#aec8e5' - }).selector(':active').css({ - 'overlay-color': 'black', - 'overlay-padding': 10, - 'overlay-opacity': 0.25 - }).selector('core') // just core properties - .css({ - 'selection-box-color': '#ddd', - 'selection-box-opacity': 0.65, - 'selection-box-border-color': '#aaa', - 'selection-box-border-width': 1, - 'active-bg-color': 'black', - 'active-bg-opacity': 0.15, - 'active-bg-size': 30, - 'outside-texture-bg-color': '#000', - 'outside-texture-bg-opacity': 0.125 - }); + r.textureMult = 2; + canvas.width = canvasWidth * r.textureMult; + canvas.height = canvasHeight * r.textureMult; + } - this.defaultLength = this.length; + r.canvasWidth = canvasWidth; + r.canvasHeight = canvasHeight; }; -module.exports = styfn; +CRp.renderTo = function (cxt, zoom, pan, pxRatio) { + this.render({ + forcedContext: cxt, + forcedZoom: zoom, + forcedPan: pan, + drawAllLayers: true, + forcedPxRatio: pxRatio + }); +}; -/***/ }), -/* 129 */ -/***/ (function(module, exports, __webpack_require__) { +CRp.render = function (options) { + options = options || util.staticEmptyObject(); -"use strict"; + var forcedContext = options.forcedContext; + var drawAllLayers = options.drawAllLayers; + var drawOnlyNodeLayer = options.drawOnlyNodeLayer; + var forcedZoom = options.forcedZoom; + var forcedPan = options.forcedPan; + var r = this; + var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio; + var cy = r.cy;var data = r.data; + var needDraw = data.canvasNeedsRedraw; + var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming); + var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur; + var mbPxRatio = r.motionBlurPxRatio; + var hasCompoundNodes = cy.hasCompoundNodes(); + var inNodeDragGesture = r.hoverData.draggingEles; + var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false; + motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection; + var motionBlurFadeEffect = motionBlur; + if (!forcedContext) { + if (r.prevPxRatio !== pixelRatio) { + r.invalidateContainerClientCoordsCache(); + r.matchCanvasSize(r.container); -var util = __webpack_require__(1); -var Selector = __webpack_require__(6); + r.redrawHint('eles', true); + r.redrawHint('drag', true); + } -var styfn = {}; + r.prevPxRatio = pixelRatio; + } -styfn.appendFromString = function (string) { - var self = this; - var style = this; - var remaining = '' + string; - var selAndBlockStr = void 0; - var blockRem = void 0; - var propAndValStr = void 0; + if (!forcedContext && r.motionBlurTimeout) { + clearTimeout(r.motionBlurTimeout); + } - // remove comments from the style string - remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, ''); + if (motionBlur) { + if (r.mbFrames == null) { + r.mbFrames = 0; + } - function removeSelAndBlockFromRemaining() { - // remove the parsed selector and block from the remaining text to parse - if (remaining.length > selAndBlockStr.length) { - remaining = remaining.substr(selAndBlockStr.length); - } else { - remaining = ''; + r.mbFrames++; + + if (r.mbFrames < 3) { + // need several frames before even high quality motionblur + motionBlurFadeEffect = false; } - } - function removePropAndValFromRem() { - // remove the parsed property and value from the remaining block text to parse - if (blockRem.length > propAndValStr.length) { - blockRem = blockRem.substr(propAndValStr.length); - } else { - blockRem = ''; + // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing) + if (r.mbFrames > r.minMbLowQualFrames) { + //r.fullQualityMb = false; + r.motionBlurPxRatio = r.mbPxRBlurry; } } - while (true) { - var nothingLeftToParse = remaining.match(/^\s*$/); - if (nothingLeftToParse) { - break; - } + if (r.clearingMotionBlur) { + r.motionBlurPxRatio = 1; + } - var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/); + // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame + // because a rogue async texture frame would clear needDraw + if (r.textureDrawLastFrame && !textureDraw) { + needDraw[r.NODE] = true; + needDraw[r.SELECT_BOX] = true; + } + + var coreStyle = cy.style()._private.coreStyle; + + var zoom = cy.zoom(); + var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom; + var pan = cy.pan(); + var effectivePan = { + x: pan.x, + y: pan.y + }; - if (!selAndBlock) { - util.error('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining); - break; + var vp = { + zoom: zoom, + pan: { + x: pan.x, + y: pan.y } + }; + var prevVp = r.prevViewport; + var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y; - selAndBlockStr = selAndBlock[0]; - - // parse the selector - var selectorStr = selAndBlock[1]; - if (selectorStr !== 'core') { - var selector = new Selector(selectorStr); - if (selector._private.invalid) { - util.error('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); + // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed) + if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) { + r.motionBlurPxRatio = 1; + } - // skip this selector and block - removeSelAndBlockFromRemaining(); - continue; - } - } + if (forcedPan) { + effectivePan = forcedPan; + } - // parse the block of properties and values - var blockStr = selAndBlock[2]; - var invalidBlock = false; - blockRem = blockStr; - var props = []; + // apply pixel ratio - while (true) { - var _nothingLeftToParse = blockRem.match(/^\s*$/); - if (_nothingLeftToParse) { - break; - } + effectiveZoom *= pixelRatio; + effectivePan.x *= pixelRatio; + effectivePan.y *= pixelRatio; - var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/); + var eles = r.getCachedZSortedEles(); - if (!propAndVal) { - util.error('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr); - invalidBlock = true; - break; - } + function mbclear(context, x, y, w, h) { + var gco = context.globalCompositeOperation; - propAndValStr = propAndVal[0]; - var propStr = propAndVal[1]; - var valStr = propAndVal[2]; + context.globalCompositeOperation = 'destination-out'; + r.fillStyle(context, 255, 255, 255, r.motionBlurTransparency); + context.fillRect(x, y, w, h); - var prop = self.properties[propStr]; - if (!prop) { - util.error('Skipping property: Invalid property name in: ' + propAndValStr); + context.globalCompositeOperation = gco; + } - // skip this property in the block - removePropAndValFromRem(); - continue; - } + function setContextTransform(context, clear) { + var ePan, eZoom, w, h; - var parsedProp = style.parse(propStr, valStr); + if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) { + ePan = { + x: pan.x * mbPxRatio, + y: pan.y * mbPxRatio + }; - if (!parsedProp) { - util.error('Skipping property: Invalid property definition in: ' + propAndValStr); + eZoom = zoom * mbPxRatio; - // skip this property in the block - removePropAndValFromRem(); - continue; - } + w = r.canvasWidth * mbPxRatio; + h = r.canvasHeight * mbPxRatio; + } else { + ePan = effectivePan; + eZoom = effectiveZoom; - props.push({ - name: propStr, - val: valStr - }); - removePropAndValFromRem(); + w = r.canvasWidth; + h = r.canvasHeight; } - if (invalidBlock) { - removeSelAndBlockFromRemaining(); - break; - } + context.setTransform(1, 0, 0, 1, 0, 0); - // put the parsed block in the style - style.selector(selectorStr); - for (var i = 0; i < props.length; i++) { - var _prop = props[i]; - style.css(_prop.name, _prop.val); + if (clear === 'motionBlur') { + mbclear(context, 0, 0, w, h); + } else if (!forcedContext && (clear === undefined || clear)) { + context.clearRect(0, 0, w, h); } - removeSelAndBlockFromRemaining(); + if (!drawAllLayers) { + context.translate(ePan.x, ePan.y); + context.scale(eZoom, eZoom); + } + if (forcedPan) { + context.translate(forcedPan.x, forcedPan.y); + } + if (forcedZoom) { + context.scale(forcedZoom, forcedZoom); + } } - return style; -}; - -styfn.fromString = function (string) { - var style = this; - - style.resetToDefault(); - style.appendFromString(string); - - return style; -}; + if (!textureDraw) { + r.textureDrawLastFrame = false; + } -module.exports = styfn; + if (textureDraw) { + r.textureDrawLastFrame = true; -/***/ }), -/* 130 */ -/***/ (function(module, exports, __webpack_require__) { + var bb; -"use strict"; + if (!r.textureCache) { + r.textureCache = {}; + bb = r.textureCache.bb = cy.mutableElements().boundingBox(); -var is = __webpack_require__(0); + r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER]; -module.exports = { - // get [r, g, b] from #abc or #aabbcc - hex2tuple: function hex2tuple(hex) { - if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') { - return; - } + var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER]; - var shortHex = hex.length === 4; - var r = void 0, - g = void 0, - b = void 0; - var base = 16; + cxt.setTransform(1, 0, 0, 1, 0, 0); + cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult); - if (shortHex) { - r = parseInt(hex[1] + hex[1], base); - g = parseInt(hex[2] + hex[2], base); - b = parseInt(hex[3] + hex[3], base); - } else { - r = parseInt(hex[1] + hex[2], base); - g = parseInt(hex[3] + hex[4], base); - b = parseInt(hex[5] + hex[6], base); - } + r.render({ + forcedContext: cxt, + drawOnlyNodeLayer: true, + forcedPxRatio: pixelRatio * r.textureMult + }); - return [r, g, b]; - }, + var vp = r.textureCache.viewport = { + zoom: cy.zoom(), + pan: cy.pan(), + width: r.canvasWidth, + height: r.canvasHeight + }; - // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0) - hsl2tuple: function hsl2tuple(hsl) { - var ret = void 0; - var h = void 0, - s = void 0, - l = void 0, - a = void 0, - r = void 0, - g = void 0, - b = void 0; - function hue2rgb(p, q, t) { - if (t < 0) t += 1; - if (t > 1) t -= 1; - if (t < 1 / 6) return p + (q - p) * 6 * t; - if (t < 1 / 2) return q; - if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; - return p; + vp.mpan = { + x: (0 - vp.pan.x) / vp.zoom, + y: (0 - vp.pan.y) / vp.zoom + }; } - var m = new RegExp('^' + this.regex.hsla + '$').exec(hsl); - if (m) { - - // get hue - h = parseInt(m[1]); - if (h < 0) { - h = (360 - -1 * h % 360) % 360; - } else if (h > 360) { - h = h % 360; - } - h /= 360; // normalise on [0, 1] - - s = parseFloat(m[2]); - if (s < 0 || s > 100) { - return; - } // saturation is [0, 100] - s = s / 100; // normalise on [0, 1] - - l = parseFloat(m[3]); - if (l < 0 || l > 100) { - return; - } // lightness is [0, 100] - l = l / 100; // normalise on [0, 1] + needDraw[r.DRAG] = false; + needDraw[r.NODE] = false; - a = m[4]; - if (a !== undefined) { - a = parseFloat(a); + var context = data.contexts[r.NODE]; - if (a < 0 || a > 1) { - return; - } // alpha is [0, 1] - } + var texture = r.textureCache.texture; + var vp = r.textureCache.viewport; + bb = r.textureCache.bb; - // now, convert to rgb - // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - if (s === 0) { - r = g = b = Math.round(l * 255); // achromatic - } else { - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = Math.round(255 * hue2rgb(p, q, h + 1 / 3)); - g = Math.round(255 * hue2rgb(p, q, h)); - b = Math.round(255 * hue2rgb(p, q, h - 1 / 3)); - } + context.setTransform(1, 0, 0, 1, 0, 0); - ret = [r, g, b, a]; + if (motionBlur) { + mbclear(context, 0, 0, vp.width, vp.height); + } else { + context.clearRect(0, 0, vp.width, vp.height); } - return ret; - }, + var outsideBgColor = coreStyle['outside-texture-bg-color'].value; + var outsideBgOpacity = coreStyle['outside-texture-bg-opacity'].value; + r.fillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity); + context.fillRect(0, 0, vp.width, vp.height); - // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0) - rgb2tuple: function rgb2tuple(rgb) { - var ret = void 0; + var zoom = cy.zoom(); - var m = new RegExp('^' + this.regex.rgba + '$').exec(rgb); - if (m) { - ret = []; + setContextTransform(context, false); - var isPct = []; - for (var i = 1; i <= 3; i++) { - var channel = m[i]; + context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio); + context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio); + } else if (r.textureOnViewport && !forcedContext) { + // clear the cache since we don't need it + r.textureCache = null; + } - if (channel[channel.length - 1] === '%') { - isPct[i] = true; - } - channel = parseFloat(channel); + var extent = cy.extent(); + var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles; + var hideEdges = r.hideEdgesOnViewport && vpManip; - if (isPct[i]) { - channel = channel / 100 * 255; // normalise to [0, 255] - } + var needMbClear = []; - if (channel < 0 || channel > 255) { - return; - } // invalid channel value + needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur; + if (needMbClear[r.NODE]) { + r.clearedForMotionBlur[r.NODE] = true; + } - ret.push(Math.floor(channel)); - } + needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur; + if (needMbClear[r.DRAG]) { + r.clearedForMotionBlur[r.DRAG] = true; + } - var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3]; - var allArePct = isPct[1] && isPct[2] && isPct[3]; - if (atLeastOneIsPct && !allArePct) { - return; - } // must all be percent values if one is + if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) { + var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1; + var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]); + var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined; - var alpha = m[4]; - if (alpha !== undefined) { - alpha = parseFloat(alpha); + setContextTransform(context, clear); - if (alpha < 0 || alpha > 1) { - return; - } // invalid alpha value + if (hideEdges) { + r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent); + } else { + r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent); + } - ret.push(alpha); - } + if (r.debug) { + r.drawDebugPoints(context, eles.nondrag); } - return ret; - }, + if (!drawAllLayers && !motionBlur) { + needDraw[r.NODE] = false; + } + } - colorname2tuple: function colorname2tuple(color) { - return this.colors[color.toLowerCase()]; - }, + if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) { + var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1; + var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]); - color2tuple: function color2tuple(color) { - return (is.array(color) ? color : null) || this.colorname2tuple(color) || this.hex2tuple(color) || this.rgb2tuple(color) || this.hsl2tuple(color); - }, + setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined); - colors: { - // special colour names - transparent: [0, 0, 0, 0], // NB alpha === 0 + if (hideEdges) { + r.drawCachedNodes(context, eles.drag, pixelRatio, extent); + } else { + r.drawCachedElements(context, eles.drag, pixelRatio, extent); + } - // regular colours - aliceblue: [240, 248, 255], - antiquewhite: [250, 235, 215], - aqua: [0, 255, 255], - aquamarine: [127, 255, 212], - azure: [240, 255, 255], - beige: [245, 245, 220], - bisque: [255, 228, 196], - black: [0, 0, 0], - blanchedalmond: [255, 235, 205], - blue: [0, 0, 255], - blueviolet: [138, 43, 226], - brown: [165, 42, 42], - burlywood: [222, 184, 135], - cadetblue: [95, 158, 160], - chartreuse: [127, 255, 0], - chocolate: [210, 105, 30], - coral: [255, 127, 80], - cornflowerblue: [100, 149, 237], - cornsilk: [255, 248, 220], - crimson: [220, 20, 60], - cyan: [0, 255, 255], - darkblue: [0, 0, 139], - darkcyan: [0, 139, 139], - darkgoldenrod: [184, 134, 11], - darkgray: [169, 169, 169], - darkgreen: [0, 100, 0], - darkgrey: [169, 169, 169], - darkkhaki: [189, 183, 107], - darkmagenta: [139, 0, 139], - darkolivegreen: [85, 107, 47], - darkorange: [255, 140, 0], - darkorchid: [153, 50, 204], - darkred: [139, 0, 0], - darksalmon: [233, 150, 122], - darkseagreen: [143, 188, 143], - darkslateblue: [72, 61, 139], - darkslategray: [47, 79, 79], - darkslategrey: [47, 79, 79], - darkturquoise: [0, 206, 209], - darkviolet: [148, 0, 211], - deeppink: [255, 20, 147], - deepskyblue: [0, 191, 255], - dimgray: [105, 105, 105], - dimgrey: [105, 105, 105], - dodgerblue: [30, 144, 255], - firebrick: [178, 34, 34], - floralwhite: [255, 250, 240], - forestgreen: [34, 139, 34], - fuchsia: [255, 0, 255], - gainsboro: [220, 220, 220], - ghostwhite: [248, 248, 255], - gold: [255, 215, 0], - goldenrod: [218, 165, 32], - gray: [128, 128, 128], - grey: [128, 128, 128], - green: [0, 128, 0], - greenyellow: [173, 255, 47], - honeydew: [240, 255, 240], - hotpink: [255, 105, 180], - indianred: [205, 92, 92], - indigo: [75, 0, 130], - ivory: [255, 255, 240], - khaki: [240, 230, 140], - lavender: [230, 230, 250], - lavenderblush: [255, 240, 245], - lawngreen: [124, 252, 0], - lemonchiffon: [255, 250, 205], - lightblue: [173, 216, 230], - lightcoral: [240, 128, 128], - lightcyan: [224, 255, 255], - lightgoldenrodyellow: [250, 250, 210], - lightgray: [211, 211, 211], - lightgreen: [144, 238, 144], - lightgrey: [211, 211, 211], - lightpink: [255, 182, 193], - lightsalmon: [255, 160, 122], - lightseagreen: [32, 178, 170], - lightskyblue: [135, 206, 250], - lightslategray: [119, 136, 153], - lightslategrey: [119, 136, 153], - lightsteelblue: [176, 196, 222], - lightyellow: [255, 255, 224], - lime: [0, 255, 0], - limegreen: [50, 205, 50], - linen: [250, 240, 230], - magenta: [255, 0, 255], - maroon: [128, 0, 0], - mediumaquamarine: [102, 205, 170], - mediumblue: [0, 0, 205], - mediumorchid: [186, 85, 211], - mediumpurple: [147, 112, 219], - mediumseagreen: [60, 179, 113], - mediumslateblue: [123, 104, 238], - mediumspringgreen: [0, 250, 154], - mediumturquoise: [72, 209, 204], - mediumvioletred: [199, 21, 133], - midnightblue: [25, 25, 112], - mintcream: [245, 255, 250], - mistyrose: [255, 228, 225], - moccasin: [255, 228, 181], - navajowhite: [255, 222, 173], - navy: [0, 0, 128], - oldlace: [253, 245, 230], - olive: [128, 128, 0], - olivedrab: [107, 142, 35], - orange: [255, 165, 0], - orangered: [255, 69, 0], - orchid: [218, 112, 214], - palegoldenrod: [238, 232, 170], - palegreen: [152, 251, 152], - paleturquoise: [175, 238, 238], - palevioletred: [219, 112, 147], - papayawhip: [255, 239, 213], - peachpuff: [255, 218, 185], - peru: [205, 133, 63], - pink: [255, 192, 203], - plum: [221, 160, 221], - powderblue: [176, 224, 230], - purple: [128, 0, 128], - red: [255, 0, 0], - rosybrown: [188, 143, 143], - royalblue: [65, 105, 225], - saddlebrown: [139, 69, 19], - salmon: [250, 128, 114], - sandybrown: [244, 164, 96], - seagreen: [46, 139, 87], - seashell: [255, 245, 238], - sienna: [160, 82, 45], - silver: [192, 192, 192], - skyblue: [135, 206, 235], - slateblue: [106, 90, 205], - slategray: [112, 128, 144], - slategrey: [112, 128, 144], - snow: [255, 250, 250], - springgreen: [0, 255, 127], - steelblue: [70, 130, 180], - tan: [210, 180, 140], - teal: [0, 128, 128], - thistle: [216, 191, 216], - tomato: [255, 99, 71], - turquoise: [64, 224, 208], - violet: [238, 130, 238], - wheat: [245, 222, 179], - white: [255, 255, 255], - whitesmoke: [245, 245, 245], - yellow: [255, 255, 0], - yellowgreen: [154, 205, 50] + if (r.debug) { + r.drawDebugPoints(context, eles.drag); + } + + if (!drawAllLayers && !motionBlur) { + needDraw[r.DRAG] = false; + } } -}; -/***/ }), -/* 131 */ -/***/ (function(module, exports, __webpack_require__) { + if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) { + var context = forcedContext || data.contexts[r.SELECT_BOX]; -"use strict"; + setContextTransform(context); + if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) { + var zoom = r.cy.zoom(); + var borderWidth = coreStyle['selection-box-border-width'].value / zoom; -var is = __webpack_require__(0); + context.lineWidth = borderWidth; + context.fillStyle = 'rgba(' + coreStyle['selection-box-color'].value[0] + ',' + coreStyle['selection-box-color'].value[1] + ',' + coreStyle['selection-box-color'].value[2] + ',' + coreStyle['selection-box-opacity'].value + ')'; -module.exports = { - // has anything been set in the map - mapEmpty: function mapEmpty(map) { - var empty = true; + context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]); - if (map != null) { - return Object.keys(map).length === 0; + if (borderWidth > 0) { + context.strokeStyle = 'rgba(' + coreStyle['selection-box-border-color'].value[0] + ',' + coreStyle['selection-box-border-color'].value[1] + ',' + coreStyle['selection-box-border-color'].value[2] + ',' + coreStyle['selection-box-opacity'].value + ')'; + + context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]); + } } - return empty; - }, + if (data.bgActivePosistion && !r.hoverData.selecting) { + var zoom = r.cy.zoom(); + var pos = data.bgActivePosistion; - // pushes to the array at the end of a map (map may not be built) - pushMap: function pushMap(options) { - var array = this.getMap(options); + context.fillStyle = 'rgba(' + coreStyle['active-bg-color'].value[0] + ',' + coreStyle['active-bg-color'].value[1] + ',' + coreStyle['active-bg-color'].value[2] + ',' + coreStyle['active-bg-opacity'].value + ')'; - if (array == null) { - // if empty, put initial array - this.setMap(this.extend({}, options, { - value: [options.value] - })); - } else { - array.push(options.value); + context.beginPath(); + context.arc(pos.x, pos.y, coreStyle['active-bg-size'].pfValue / zoom, 0, 2 * Math.PI); + context.fill(); } - }, - // sets the value in a map (map may not be built) - setMap: function setMap(options) { - var obj = options.map; - var key = void 0; - var keys = options.keys; - var l = keys.length; + var timeToRender = r.lastRedrawTime; + if (r.showFps && timeToRender) { + timeToRender = Math.round(timeToRender); + var fps = Math.round(1000 / timeToRender); - for (var i = 0; i < l; i++) { - var _key = keys[i]; + context.setTransform(1, 0, 0, 1, 0, 0); - if (is.plainObject(_key)) { - this.error('Tried to set map with object key'); - } + context.fillStyle = 'rgba(255, 0, 0, 0.75)'; + context.strokeStyle = 'rgba(255, 0, 0, 0.75)'; + context.lineWidth = 1; + context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20); - if (i < keys.length - 1) { + var maxFps = 60; + context.strokeRect(0, 30, 250, 20); + context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20); + } - // extend the map if necessary - if (obj[_key] == null) { - obj[_key] = {}; - } + if (!drawAllLayers) { + needDraw[r.SELECT_BOX] = false; + } + } - obj = obj[_key]; + // motionblur: blit rendered blurry frames + if (motionBlur && mbPxRatio !== 1) { + var cxtNode = data.contexts[r.NODE]; + var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE]; + + var cxtDrag = data.contexts[r.DRAG]; + var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]; + + var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) { + cxt.setTransform(1, 0, 0, 1, 0, 0); + + if (needClear || !motionBlurFadeEffect) { + cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight); } else { - // set the value - obj[_key] = options.value; + mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight); } + + var pxr = mbPxRatio; + + cxt.drawImage(txt, // img + 0, 0, // sx, sy + r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh + 0, 0, // x, y + r.canvasWidth, r.canvasHeight // w, h + ); + }; + + if (needDraw[r.NODE] || needMbClear[r.NODE]) { + drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]); + needDraw[r.NODE] = false; } - }, - // gets the value in a map even if it's not built in places - getMap: function getMap(options) { - var obj = options.map; - var keys = options.keys; - var l = keys.length; + if (needDraw[r.DRAG] || needMbClear[r.DRAG]) { + drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]); + needDraw[r.DRAG] = false; + } + } - for (var i = 0; i < l; i++) { - var key = keys[i]; + r.prevViewport = vp; - if (is.plainObject(key)) { - this.error('Tried to get map with object key'); - } + if (r.clearingMotionBlur) { + r.clearingMotionBlur = false; + r.motionBlurCleared = true; + r.motionBlur = true; + } - obj = obj[key]; + if (motionBlur) { + r.motionBlurTimeout = setTimeout(function () { + r.motionBlurTimeout = null; - if (obj == null) { - return obj; - } - } + r.clearedForMotionBlur[r.NODE] = false; + r.clearedForMotionBlur[r.DRAG] = false; + r.motionBlur = false; + r.clearingMotionBlur = !textureDraw; + r.mbFrames = 0; - return obj; - }, + needDraw[r.NODE] = true; + needDraw[r.DRAG] = true; - // deletes the entry in the map - deleteMap: function deleteMap(options) { - var obj = options.map; - var keys = options.keys; - var l = keys.length; - var keepChildren = options.keepChildren; + r.redraw(); + }, motionBlurDelay); + } - for (var i = 0; i < l; i++) { - var key = keys[i]; + if (!forcedContext) { + cy.emit('render'); + } +}; - if (is.plainObject(key)) { - this.error('Tried to delete map with object key'); - } +module.exports = CRp; + +/***/ }), +/* 133 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var math = __webpack_require__(2); + +var CRp = {}; + +// @O Polygon drawing +CRp.drawPolygonPath = function (context, x, y, width, height, points) { + + var halfW = width / 2; + var halfH = height / 2; + + if (context.beginPath) { + context.beginPath(); + } + + context.moveTo(x + halfW * points[0], y + halfH * points[1]); + + for (var i = 1; i < points.length / 2; i++) { + context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]); + } + + context.closePath(); +}; + +// Round rectangle drawing +CRp.drawRoundRectanglePath = function (context, x, y, width, height) { + + var halfWidth = width / 2; + var halfHeight = height / 2; + var cornerRadius = math.getRoundRectangleRadius(width, height); + + if (context.beginPath) { + context.beginPath(); + } + + // Start at top middle + context.moveTo(x, y - halfHeight); + // Arc from middle top to right side + context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); + // Arc from right side to bottom + context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); + // Arc from bottom to left side + context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); + // Arc from left side to topBorder + context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); + // Join line + context.lineTo(x, y - halfHeight); + + context.closePath(); +}; + +CRp.drawBottomRoundRectanglePath = function (context, x, y, width, height) { + + var halfWidth = width / 2; + var halfHeight = height / 2; + var cornerRadius = math.getRoundRectangleRadius(width, height); + + if (context.beginPath) { + context.beginPath(); + } + + // Start at top middle + context.moveTo(x, y - halfHeight); + context.lineTo(x + halfWidth, y - halfHeight); + context.lineTo(x + halfWidth, y); + + context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); + context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); + + context.lineTo(x - halfWidth, y - halfHeight); + context.lineTo(x, y - halfHeight); + + context.closePath(); +}; + +CRp.drawCutRectanglePath = function (context, x, y, width, height) { + + var halfWidth = width / 2; + var halfHeight = height / 2; + var cornerLength = math.getCutRectangleCornerLength(); - var lastKey = i === options.keys.length - 1; - if (lastKey) { + if (context.beginPath) { + context.beginPath(); + } - if (keepChildren) { - // then only delete child fields not in keepChildren - var children = Object.keys(obj); + context.moveTo(x - halfWidth + cornerLength, y - halfHeight); - for (var j = 0; j < children.length; j++) { - var child = children[j]; + context.lineTo(x + halfWidth - cornerLength, y - halfHeight); + context.lineTo(x + halfWidth, y - halfHeight + cornerLength); + context.lineTo(x + halfWidth, y + halfHeight - cornerLength); + context.lineTo(x + halfWidth - cornerLength, y + halfHeight); + context.lineTo(x - halfWidth + cornerLength, y + halfHeight); + context.lineTo(x - halfWidth, y + halfHeight - cornerLength); + context.lineTo(x - halfWidth, y - halfHeight + cornerLength); - if (!keepChildren[child]) { - obj[child] = undefined; - } - } - } else { - obj[key] = undefined; - } - } else { - obj = obj[key]; - } - } - } + context.closePath(); }; -/***/ }), -/* 132 */ -/***/ (function(module, exports, __webpack_require__) { +CRp.drawBarrelPath = function (context, x, y, width, height) { -"use strict"; + var halfWidth = width / 2; + var halfHeight = height / 2; + var xBegin = x - halfWidth; + var xEnd = x + halfWidth; + var yBegin = y - halfHeight; + var yEnd = y + halfHeight; -var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))'; + var barrelCurveConstants = math.getBarrelCurveConstants(width, height); + var wOffset = barrelCurveConstants.widthOffset; + var hOffset = barrelCurveConstants.heightOffset; + var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset; -var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)'; -var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)'; + if (context.beginPath) { + context.beginPath(); + } -var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)'; -var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)'; + context.moveTo(xBegin, yBegin + hOffset); -var hex3 = '\\#[0-9a-fA-F]{3}'; -var hex6 = '\\#[0-9a-fA-F]{6}'; + context.lineTo(xBegin, yEnd - hOffset); + context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd); -module.exports = { - regex: { - number: number, - rgba: rgba, - rgbaNoBackRefs: rgbaNoBackRefs, - hsla: hsla, - hslaNoBackRefs: hslaNoBackRefs, - hex3: hex3, - hex6: hex6 - } + context.lineTo(xEnd - wOffset, yEnd); + context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset); + + context.lineTo(xEnd, yBegin + hOffset); + context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin); + + context.lineTo(xBegin + wOffset, yBegin); + context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset); + + context.closePath(); }; -/***/ }), -/* 133 */ -/***/ (function(module, exports, __webpack_require__) { +var sin0 = Math.sin(0); +var cos0 = Math.cos(0); -"use strict"; +var sin = {}; +var cos = {}; +var ellipseStepSize = Math.PI / 40; -function ascending(a, b) { - if (a < b) { - return -1; - } else if (a > b) { - return 1; - } else { - return 0; - } +for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) { + sin[i] = Math.sin(i); + cos[i] = Math.cos(i); } -function descending(a, b) { - return -1 * ascending(a, b); -} +CRp.drawEllipsePath = function (context, centerX, centerY, width, height) { + if (context.beginPath) { + context.beginPath(); + } -module.exports = { - sort: { - ascending: ascending, - descending: descending + if (context.ellipse) { + context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI); + } else { + var xPos, yPos; + var rw = width / 2; + var rh = height / 2; + for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) { + xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0; + yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0; + + if (i === 0) { + context.moveTo(xPos, yPos); + } else { + context.lineTo(xPos, yPos); + } + } } + + context.closePath(); }; +module.exports = CRp; + /***/ }), /* 134 */ /***/ (function(module, exports, __webpack_require__) { @@ -28950,581 +28754,304 @@ module.exports = { "use strict"; -var memoize = __webpack_require__(19); var is = __webpack_require__(0); -module.exports = { +var CRp = {}; - camel2dash: memoize(function (str) { - return str.replace(/([A-Z])/g, function (v) { - return '-' + v.toLowerCase(); - }); - }), +CRp.createBuffer = function (w, h) { + var buffer = document.createElement('canvas'); // eslint-disable-line no-undef + buffer.width = w; + buffer.height = h; - dash2camel: memoize(function (str) { - return str.replace(/(-\w)/g, function (v) { - return v[1].toUpperCase(); - }); - }), + return [buffer, buffer.getContext('2d')]; +}; - prependCamel: memoize(function (prefix, str) { - return prefix + str[0].toUpperCase() + str.substring(1); - }, function (prefix, str) { - return prefix + '$' + str; - }), +CRp.bufferCanvasImage = function (options) { + var cy = this.cy; + var eles = cy.mutableElements(); + var bb = eles.boundingBox(); + var ctrRect = this.findContainerClientCoords(); + var width = options.full ? Math.ceil(bb.w) : ctrRect[2]; + var height = options.full ? Math.ceil(bb.h) : ctrRect[3]; + var specdMaxDims = is.number(options.maxWidth) || is.number(options.maxHeight); + var pxRatio = this.getPixelRatio(); + var scale = 1; - capitalize: function capitalize(str) { - if (is.emptyString(str)) { - return str; + if (options.scale !== undefined) { + width *= options.scale; + height *= options.scale; + + scale = options.scale; + } else if (specdMaxDims) { + var maxScaleW = Infinity; + var maxScaleH = Infinity; + + if (is.number(options.maxWidth)) { + maxScaleW = scale * options.maxWidth / width; } - return str.charAt(0).toUpperCase() + str.substring(1); - } + if (is.number(options.maxHeight)) { + maxScaleH = scale * options.maxHeight / height; + } -}; + scale = Math.min(maxScaleW, maxScaleH); -/***/ }), -/* 135 */ -/***/ (function(module, exports, __webpack_require__) { + width *= scale; + height *= scale; + } -"use strict"; + if (!specdMaxDims) { + width *= pxRatio; + height *= pxRatio; + scale *= pxRatio; + } + var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef -var window = __webpack_require__(4); -var performance = window ? window.performance : null; + buffCanvas.width = width; + buffCanvas.height = height; -var util = {}; + buffCanvas.style.width = width + 'px'; + buffCanvas.style.height = height + 'px'; -var pnow = performance && performance.now ? function () { - return performance.now(); -} : function () { - return Date.now(); -}; + var buffCxt = buffCanvas.getContext('2d'); -var raf = function () { - if (window) { - if (window.requestAnimationFrame) { - return function (fn) { - window.requestAnimationFrame(fn); - }; - } else if (window.mozRequestAnimationFrame) { - return function (fn) { - window.mozRequestAnimationFrame(fn); - }; - } else if (window.webkitRequestAnimationFrame) { - return function (fn) { - window.webkitRequestAnimationFrame(fn); - }; - } else if (window.msRequestAnimationFrame) { - return function (fn) { - window.msRequestAnimationFrame(fn); - }; - } - } + // Rasterize the layers, but only if container has nonzero size + if (width > 0 && height > 0) { - return function (fn) { - if (fn) { - setTimeout(function () { - fn(pnow()); - }, 1000 / 60); - } - }; -}(); + buffCxt.clearRect(0, 0, width, height); -util.requestAnimationFrame = function (fn) { - raf(fn); -}; + buffCxt.globalCompositeOperation = 'source-over'; -util.performanceNow = pnow; + var zsortedEles = this.getCachedZSortedEles(); -util.debounce = __webpack_require__(141); + if (options.full) { + // draw the full bounds of the graph + buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale); + buffCxt.scale(scale, scale); -util.now = function () { - return Date.now(); -}; + this.drawElements(buffCxt, zsortedEles); -module.exports = util; + buffCxt.scale(1 / scale, 1 / scale); + buffCxt.translate(bb.x1 * scale, bb.y1 * scale); + } else { + // draw the current view + var pan = cy.pan(); -/***/ }), -/* 136 */ -/***/ (function(module, exports) { + var translation = { + x: pan.x * scale, + y: pan.y * scale + }; -// shim for using process in browser -var process = module.exports = {}; + scale *= cy.zoom(); -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. + buffCxt.translate(translation.x, translation.y); + buffCxt.scale(scale, scale); -var cachedSetTimeout; -var cachedClearTimeout; + this.drawElements(buffCxt, zsortedEles); -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } + buffCxt.scale(1 / scale, 1 / scale); + buffCxt.translate(-translation.x, -translation.y); } + // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs + if (options.bg) { + buffCxt.globalCompositeOperation = 'destination-over'; -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } + buffCxt.fillStyle = options.bg; + buffCxt.rect(0, 0, width, height); + buffCxt.fill(); } + } + return buffCanvas; +}; +function b64ToBlob(b64, mimeType) { + var bytes = atob(b64); + var buff = new ArrayBuffer(bytes.length); + var buffUint8 = new Uint8Array(buff); -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; + for (var i = 0; i < bytes.length; i++) { + buffUint8[i] = bytes.charCodeAt(i); + } -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } + return new Blob([buff], { type: mimeType }); } -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} +function b64UriToB64(b64uri) { + var i = b64uri.indexOf(','); -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } + return b64uri.substr(i + 1); }; -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; +function output(options, canvas, mimeType) { + var b64Uri = canvas.toDataURL(mimeType, options.quality); -function noop() {} + switch (options.output) { + case 'blob': + return b64ToBlob(b64UriToB64(b64Uri), mimeType); -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; + case 'base64': + return b64UriToB64(b64Uri); -process.listeners = function (name) { return [] } + case 'base64uri': + default: + return b64Uri; + } +} -process.binding = function (name) { - throw new Error('process.binding is not supported'); +CRp.png = function (options) { + return output(options, this.bufferCanvasImage(options), 'image/png'); }; -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); +CRp.jpg = function (options) { + return output(options, this.bufferCanvasImage(options), 'image/jpeg'); }; -process.umask = function() { return 0; }; +module.exports = CRp; /***/ }), -/* 137 */ +/* 135 */ /***/ (function(module, exports, __webpack_require__) { -/* WEBPACK VAR INJECTION */(function(global, process) {(function (global, undefined) { - "use strict"; - - if (global.setImmediate) { - return; - } +"use strict"; - var nextHandle = 1; // Spec says greater than zero - var tasksByHandle = {}; - var currentlyRunningATask = false; - var doc = global.document; - var registerImmediate; - function setImmediate(callback) { - // Callback can either be a function or a string - if (typeof callback !== "function") { - callback = new Function("" + callback); - } - // Copy function arguments - var args = new Array(arguments.length - 1); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i + 1]; - } - // Store and register the task - var task = { callback: callback, args: args }; - tasksByHandle[nextHandle] = task; - registerImmediate(nextHandle); - return nextHandle++; - } +var CRp = {}; - function clearImmediate(handle) { - delete tasksByHandle[handle]; - } +CRp.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) { + switch (name) { + case 'ellipse': + return this.drawEllipsePath(context, centerX, centerY, width, height); + case 'polygon': + return this.drawPolygonPath(context, centerX, centerY, width, height, points); + case 'roundrectangle': + return this.drawRoundRectanglePath(context, centerX, centerY, width, height); + case 'cutrectangle': + return this.drawCutRectanglePath(context, centerX, centerY, width, height); + case 'bottomroundrectangle': + return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height); + case 'barrel': + return this.drawBarrelPath(context, centerX, centerY, width, height); + } +}; - function run(task) { - var callback = task.callback; - var args = task.args; - switch (args.length) { - case 0: - callback(); - break; - case 1: - callback(args[0]); - break; - case 2: - callback(args[0], args[1]); - break; - case 3: - callback(args[0], args[1], args[2]); - break; - default: - callback.apply(undefined, args); - break; - } - } +module.exports = CRp; - function runIfPresent(handle) { - // From the spec: "Wait until any invocations of this algorithm started before this one have completed." - // So if we're currently running a task, we'll need to delay this invocation. - if (currentlyRunningATask) { - // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a - // "too much recursion" error. - setTimeout(runIfPresent, 0, handle); - } else { - var task = tasksByHandle[handle]; - if (task) { - currentlyRunningATask = true; - try { - run(task); - } finally { - clearImmediate(handle); - currentlyRunningATask = false; - } - } - } - } +/***/ }), +/* 136 */ +/***/ (function(module, exports, __webpack_require__) { - function installNextTickImplementation() { - registerImmediate = function(handle) { - process.nextTick(function () { runIfPresent(handle); }); - }; - } +"use strict"; - function canUsePostMessage() { - // The test against `importScripts` prevents this implementation from being installed inside a web worker, - // where `global.postMessage` means something completely different and can't be used for this purpose. - if (global.postMessage && !global.importScripts) { - var postMessageIsAsynchronous = true; - var oldOnMessage = global.onmessage; - global.onmessage = function() { - postMessageIsAsynchronous = false; - }; - global.postMessage("", "*"); - global.onmessage = oldOnMessage; - return postMessageIsAsynchronous; - } - } - function installPostMessageImplementation() { - // Installs an event handler on `global` for the `message` event: see - // * https://developer.mozilla.org/en/DOM/window.postMessage - // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages +var is = __webpack_require__(0); +var util = __webpack_require__(1); +var Style = __webpack_require__(18); - var messagePrefix = "setImmediate$" + Math.random() + "$"; - var onGlobalMessage = function(event) { - if (event.source === global && - typeof event.data === "string" && - event.data.indexOf(messagePrefix) === 0) { - runIfPresent(+event.data.slice(messagePrefix.length)); - } - }; +// a dummy stylesheet object that doesn't need a reference to the core +// (useful for init) +var Stylesheet = function Stylesheet() { + if (!(this instanceof Stylesheet)) { + return new Stylesheet(); + } - if (global.addEventListener) { - global.addEventListener("message", onGlobalMessage, false); - } else { - global.attachEvent("onmessage", onGlobalMessage); - } + this.length = 0; +}; - registerImmediate = function(handle) { - global.postMessage(messagePrefix + handle, "*"); - }; - } +var sheetfn = Stylesheet.prototype; - function installMessageChannelImplementation() { - var channel = new MessageChannel(); - channel.port1.onmessage = function(event) { - var handle = event.data; - runIfPresent(handle); - }; +sheetfn.instanceString = function () { + return 'stylesheet'; +}; - registerImmediate = function(handle) { - channel.port2.postMessage(handle); - }; - } +// just store the selector to be parsed later +sheetfn.selector = function (selector) { + var i = this.length++; - function installReadyStateChangeImplementation() { - var html = doc.documentElement; - registerImmediate = function(handle) { - // Create a