From 6b59540a2460633df4e3d8347cb4dfe2fb3a3afb Mon Sep 17 00:00:00 2001 From: Cyril Cohen Date: Wed, 16 Oct 2019 11:26:43 +0200 Subject: removing everything but index which redirects to the new page --- docs/htmldoc/js/cytoscape-dagre.js | 192 - docs/htmldoc/js/cytoscape-panzoom.js | 550 - docs/htmldoc/js/cytoscape-qtip.js | 362 - docs/htmldoc/js/cytoscape.js | 24515 ----------------------------- docs/htmldoc/js/cytoscape.js-panzoom.css | 203 - docs/htmldoc/js/cytoscape.min.js | 26 - docs/htmldoc/js/dagre.js | 16396 ------------------- docs/htmldoc/js/dagre.min.js | 6 - docs/htmldoc/js/jquery-2.0.3.js | 8829 ----------- docs/htmldoc/js/jquery-2.0.3.min.js | 6 - docs/htmldoc/js/jquery.qtip.css | 623 - docs/htmldoc/js/jquery.qtip.js | 3440 ---- docs/htmldoc/js/jquery.qtip.min.css | 2 - docs/htmldoc/js/jquery.qtip.min.js | 4 - 14 files changed, 55154 deletions(-) delete mode 100644 docs/htmldoc/js/cytoscape-dagre.js delete mode 100644 docs/htmldoc/js/cytoscape-panzoom.js delete mode 100644 docs/htmldoc/js/cytoscape-qtip.js delete mode 100644 docs/htmldoc/js/cytoscape.js delete mode 100644 docs/htmldoc/js/cytoscape.js-panzoom.css delete mode 100644 docs/htmldoc/js/cytoscape.min.js delete mode 100644 docs/htmldoc/js/dagre.js delete mode 100644 docs/htmldoc/js/dagre.min.js delete mode 100644 docs/htmldoc/js/jquery-2.0.3.js delete mode 100644 docs/htmldoc/js/jquery-2.0.3.min.js delete mode 100644 docs/htmldoc/js/jquery.qtip.css delete mode 100644 docs/htmldoc/js/jquery.qtip.js delete mode 100644 docs/htmldoc/js/jquery.qtip.min.css delete mode 100644 docs/htmldoc/js/jquery.qtip.min.js (limited to 'docs/htmldoc/js') diff --git a/docs/htmldoc/js/cytoscape-dagre.js b/docs/htmldoc/js/cytoscape-dagre.js deleted file mode 100644 index c93288b..0000000 --- a/docs/htmldoc/js/cytoscape-dagre.js +++ /dev/null @@ -1,192 +0,0 @@ -;(function(){ 'use strict'; - - // registers the extension on a cytoscape lib ref - var register = function( cytoscape, dagre ){ - if( !cytoscape || !dagre ){ return; } // can't register if cytoscape unspecified - - var isFunction = function(o){ return typeof o === 'function'; }; - - // default layout options - var defaults = { - // dagre algo options, uses default value on undefined - nodeSep: undefined, // the separation between adjacent nodes in the same rank - edgeSep: undefined, // the separation between adjacent edges in the same rank - rankSep: undefined, // the separation between adjacent nodes in the same rank - rankDir: undefined, // 'TB' for top to bottom flow, 'LR' for left to right - minLen: function( edge ){ return 1; }, // number of ranks to keep between the source and target of the edge - edgeWeight: function( edge ){ return 1; }, // higher weight edges are generally made shorter and straighter than lower weight edges - - // general layout options - fit: true, // whether to fit to viewport - padding: 30, // fit padding - animate: false, // whether to transition the node positions - animationDuration: 500, // duration of animation in ms if enabled - animationEasing: undefined, // easing of animation if enabled - boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } - ready: function(){}, // on layoutready - stop: function(){} // on layoutstop - }; - - // constructor - // options : object containing layout options - function DagreLayout( options ){ - var opts = this.options = {}; - for( var i in defaults ){ opts[i] = defaults[i]; } - for( var i in options ){ opts[i] = options[i]; } - } - - // runs the layout - DagreLayout.prototype.run = function(){ - var options = this.options; - var layout = this; - - var cy = options.cy; // cy is automatically populated for us in the constructor - var eles = options.eles; - - var getVal = function( ele, val ){ - return isFunction(val) ? val.apply( ele, [ ele ] ) : val; - }; - - var bb = options.boundingBox || { x1: 0, y1: 0, w: cy.width(), h: cy.height() }; - if( bb.x2 === undefined ){ bb.x2 = bb.x1 + bb.w; } - if( bb.w === undefined ){ bb.w = bb.x2 - bb.x1; } - if( bb.y2 === undefined ){ bb.y2 = bb.y1 + bb.h; } - if( bb.h === undefined ){ bb.h = bb.y2 - bb.y1; } - - var g = new dagre.graphlib.Graph({ - multigraph: true, - compound: true - }); - - var gObj = {}; - var setGObj = function( name, val ){ - if( val != null ){ - gObj[ name ] = val; - } - }; - - setGObj( 'nodesep', options.nodeSep ); - setGObj( 'edgesep', options.edgeSep ); - setGObj( 'ranksep', options.rankSep ); - setGObj( 'rankdir', options.rankDir ); - - g.setGraph( gObj ); - - g.setDefaultEdgeLabel(function() { return {}; }); - g.setDefaultNodeLabel(function() { return {}; }); - - // add nodes to dagre - var nodes = eles.nodes(); - for( var i = 0; i < nodes.length; i++ ){ - var node = nodes[i]; - var nbb = node.boundingBox(); - - g.setNode( node.id(), { - width: nbb.w, - height: nbb.h, - name: node.id() - } ); - - // console.log( g.node(node.id()) ); - } - - // set compound parents - for( var i = 0; i < nodes.length; i++ ){ - var node = nodes[i]; - - if( node.isChild() ){ - g.setParent( node.id(), node.parent().id() ); - } - } - - // add edges to dagre - var edges = eles.edges().stdFilter(function( edge ){ - return !edge.source().isParent() && !edge.target().isParent(); // dagre can't handle edges on compound nodes - }); - for( var i = 0; i < edges.length; i++ ){ - var edge = edges[i]; - - g.setEdge( edge.source().id(), edge.target().id(), { - minlen: getVal( edge, options.minLen ), - weight: getVal( edge, options.edgeWeight ), - name: edge.id() - }, edge.id() ); - - // console.log( g.edge(edge.source().id(), edge.target().id(), edge.id()) ); - } - - dagre.layout( g ); - - var gNodeIds = g.nodes(); - for( var i = 0; i < gNodeIds.length; i++ ){ - var id = gNodeIds[i]; - var n = g.node( id ); - - cy.getElementById(id).scratch().dagre = n; - } - - var dagreBB; - - if( options.boundingBox ){ - dagreBB = { x1: Infinity, x2: -Infinity, y1: Infinity, y2: -Infinity }; - nodes.forEach(function( node ){ - var dModel = node.scratch().dagre; - - dagreBB.x1 = Math.min( dagreBB.x1, dModel.x ); - dagreBB.x2 = Math.max( dagreBB.x2, dModel.x ); - - dagreBB.y1 = Math.min( dagreBB.y1, dModel.y ); - dagreBB.y2 = Math.max( dagreBB.y2, dModel.y ); - }); - - dagreBB.w = dagreBB.x2 - dagreBB.x1; - dagreBB.h = dagreBB.y2 - dagreBB.y1; - } else { - dagreBB = bb; - } - - var constrainPos = function( p ){ - if( options.boundingBox ){ - var xPct = (p.x - dagreBB.x1) / dagreBB.w; - var yPct = (p.y - dagreBB.y1) / dagreBB.h; - - return { - x: bb.x1 + xPct * bb.w, - y: bb.y1 + yPct * bb.h - }; - } else { - return p; - } - }; - - nodes.layoutPositions(layout, options, function(){ - var dModel = this.scratch().dagre; - - return constrainPos({ - x: dModel.x, - y: dModel.y - }); - }); - - return this; // chaining - }; - - cytoscape('layout', 'dagre', DagreLayout); - - }; - - if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module - module.exports = register; - } - - if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module - define('cytoscape-dagre', function(){ - return register; - }); - } - - if( typeof cytoscape !== 'undefined' && typeof dagre !== 'undefined' ){ // expose to global cytoscape (i.e. window.cytoscape) - register( cytoscape, dagre ); - } - -})(); diff --git a/docs/htmldoc/js/cytoscape-panzoom.js b/docs/htmldoc/js/cytoscape-panzoom.js deleted file mode 100644 index 4680775..0000000 --- a/docs/htmldoc/js/cytoscape-panzoom.js +++ /dev/null @@ -1,550 +0,0 @@ -;(function(){ 'use strict'; - - // registers the extension on a cytoscape lib ref - var register = function( cytoscape, $ ){ - if( !cytoscape ){ return; } // can't register if cytoscape unspecified - - $.fn.cyPanzoom = $.fn.cytoscapePanzoom = function( options ){ - panzoom.apply( this, [ options ] ); - - return this; // chainability - }; - - // if you want a core extension - cytoscape('core', 'panzoom', function( options ){ // could use options object, but args are up to you - var cy = this; - - panzoom.apply( cy.container(), [ options ] ); - - return this; // chainability - }); - - }; - - var defaults = { - zoomFactor: 0.05, // zoom factor per zoom tick - zoomDelay: 45, // how many ms between zoom ticks - minZoom: 0.1, // min zoom level - maxZoom: 10, // max zoom level - fitPadding: 50, // padding when fitting - panSpeed: 10, // how many ms in between pan ticks - panDistance: 10, // max pan distance per tick - panDragAreaSize: 75, // the length of the pan drag box in which the vector for panning is calculated (bigger = finer control of pan speed and direction) - panMinPercentSpeed: 0.25, // the slowest speed we can pan by (as a percent of panSpeed) - panInactiveArea: 8, // radius of inactive area in pan drag box - panIndicatorMinOpacity: 0.5, // min opacity of pan indicator (the draggable nib); scales from this to 1.0 - zoomOnly: false, // a minimal version of the ui only with zooming (useful on systems with bad mousewheel resolution) - - // icon class names - sliderHandleIcon: 'fa fa-minus', - zoomInIcon: 'fa fa-plus', - zoomOutIcon: 'fa fa-minus', - resetIcon: 'fa fa-expand' - }; - - var panzoom = function(params){ - var options = $.extend(true, {}, defaults, params); - var fn = params; - - var functions = { - destroy: function(){ - var $this = $(this); - var $pz = $this.find(".cy-panzoom"); - - $pz.data('winbdgs').forEach(function( l ){ - $(window).unbind( l.evt, l.fn ); - }); - - $pz.data('cybdgs').forEach(function( l ){ - $(this).cytoscape('get').off( l.evt, l.fn ); - }); - - $pz.remove(); - }, - - init: function(){ - var browserIsMobile = 'ontouchstart' in window; - - return $(this).each(function(){ - var $container = $(this); - - var winbdgs = []; - var $win = $(window); - - var windowBind = function( evt, fn ){ - winbdgs.push({ evt: evt, fn: fn }); - - $win.bind( evt, fn ); - }; - - var windowUnbind = function( evt, fn ){ - for( var i = 0; i < winbdgs.length; i++ ){ - var l = winbdgs[i]; - - if( l.evt === evt && l.fn === fn ){ - winbdgs.splice( i, 1 ); - break; - } - } - - $win.unbind( evt, fn ); - }; - - var cybdgs = []; - var cy = $container.cytoscape('get'); - - var cyOn = function( evt, fn ){ - cybdgs.push({ evt: evt, fn: fn }); - - cy.on( evt, fn ); - }; - - var cyOff = function( evt, fn ){ - for( var i = 0; i < cybdgs.length; i++ ){ - var l = cybdgs[i]; - - if( l.evt === evt && l.fn === fn ){ - cybdgs.splice( i, 1 ); - break; - } - } - - cy.off( evt, fn ); - }; - - var $panzoom = $('
'); - $container.append( $panzoom ); - - $panzoom.data('winbdgs', winbdgs); - $panzoom.data('cybdgs', cybdgs); - - if( options.zoomOnly ){ - $panzoom.addClass("cy-panzoom-zoom-only"); - } - - // add base html elements - ///////////////////////// - - var $zoomIn = $('
'); - $panzoom.append( $zoomIn ); - - var $zoomOut = $('
'); - $panzoom.append( $zoomOut ); - - var $reset = $('
'); - $panzoom.append( $reset ); - - var $slider = $('
'); - $panzoom.append( $slider ); - - $slider.append('
'); - - var $sliderHandle = $('
'); - $slider.append( $sliderHandle ); - - var $noZoomTick = $('
'); - $slider.append( $noZoomTick ); - - var $panner = $('
'); - $panzoom.append( $panner ); - - var $pHandle = $('
'); - $panner.append( $pHandle ); - - var $pUp = $('
'); - var $pDown = $('
'); - var $pLeft = $('
'); - var $pRight = $('
'); - $panner.append( $pUp ).append( $pDown ).append( $pLeft ).append( $pRight ); - - var $pIndicator = $('
'); - $panner.append( $pIndicator ); - - // functions for calculating panning - //////////////////////////////////// - - function handle2pan(e){ - var v = { - x: e.originalEvent.pageX - $panner.offset().left - $panner.width()/2, - y: e.originalEvent.pageY - $panner.offset().top - $panner.height()/2 - } - - var r = options.panDragAreaSize; - var d = Math.sqrt( v.x*v.x + v.y*v.y ); - var percent = Math.min( d/r, 1 ); - - if( d < options.panInactiveArea ){ - return { - x: NaN, - y: NaN - }; - } - - v = { - x: v.x/d, - y: v.y/d - }; - - percent = Math.max( options.panMinPercentSpeed, percent ); - - var vnorm = { - x: -1 * v.x * (percent * options.panDistance), - y: -1 * v.y * (percent * options.panDistance) - }; - - return vnorm; - } - - function donePanning(){ - clearInterval(panInterval); - windowUnbind("mousemove", handler); - - $pIndicator.hide(); - } - - function positionIndicator(pan){ - var v = pan; - var d = Math.sqrt( v.x*v.x + v.y*v.y ); - var vnorm = { - x: -1 * v.x/d, - y: -1 * v.y/d - }; - - var w = $panner.width(); - var h = $panner.height(); - var percent = d/options.panDistance; - var opacity = Math.max( options.panIndicatorMinOpacity, percent ); - var color = 255 - Math.round( opacity * 255 ); - - $pIndicator.show().css({ - left: w/2 * vnorm.x + w/2, - top: h/2 * vnorm.y + h/2, - background: "rgb(" + color + ", " + color + ", " + color + ")" - }); - } - - function calculateZoomCenterPoint(){ - var cy = $container.cytoscape("get"); - var pan = cy.pan(); - var zoom = cy.zoom(); - - zx = $container.width()/2; - zy = $container.height()/2; - } - - var zooming = false; - function startZooming(){ - zooming = true; - - calculateZoomCenterPoint(); - } - - - function endZooming(){ - zooming = false; - } - - var zx, zy; - function zoomTo(level){ - var cy = $container.cytoscape("get"); - - if( !zooming ){ // for non-continuous zooming (e.g. click slider at pt) - calculateZoomCenterPoint(); - } - - cy.zoom({ - level: level, - renderedPosition: { x: zx, y: zy } - }); - } - - var panInterval; - - var handler = function(e){ - e.stopPropagation(); // don't trigger dragging of panzoom - e.preventDefault(); // don't cause text selection - clearInterval(panInterval); - - var pan = handle2pan(e); - - if( isNaN(pan.x) || isNaN(pan.y) ){ - $pIndicator.hide(); - return; - } - - positionIndicator(pan); - panInterval = setInterval(function(){ - $container.cytoscape("get").panBy(pan); - }, options.panSpeed); - }; - - $pHandle.bind("mousedown", function(e){ - // handle click of icon - handler(e); - - // update on mousemove - windowBind("mousemove", handler); - }); - - $pHandle.bind("mouseup", function(){ - donePanning(); - }); - - windowBind("mouseup blur", function(){ - donePanning(); - }); - - - - // set up slider behaviour - ////////////////////////// - - $slider.bind('mousedown', function(){ - return false; // so we don't pan close to the slider handle - }); - - var sliderVal; - var sliding = false; - var sliderPadding = 2; - - function setSliderFromMouse(evt, handleOffset){ - if( handleOffset === undefined ){ - handleOffset = 0; - } - - var padding = sliderPadding; - var min = 0 + padding; - var max = $slider.height() - $sliderHandle.height() - 2*padding; - var top = evt.pageY - $slider.offset().top - handleOffset; - - // constrain to slider bounds - if( top < min ){ top = min } - if( top > max ){ top = max } - - var percent = 1 - (top - min) / ( max - min ); - - // move the handle - $sliderHandle.css('top', top); - - var zmin = options.minZoom; - var zmax = options.maxZoom; - - // assume (zoom = zmax ^ p) where p ranges on (x, 1) with x negative - var x = Math.log(zmin) / Math.log(zmax); - var p = (1 - x)*percent + x; - - // change the zoom level - var z = Math.pow( zmax, p ); - - // bound the zoom value in case of floating pt rounding error - if( z < zmin ){ - z = zmin; - } else if( z > zmax ){ - z = zmax; - } - - zoomTo( z ); - } - - var sliderMdownHandler, sliderMmoveHandler; - $sliderHandle.bind('mousedown', sliderMdownHandler = function( mdEvt ){ - var handleOffset = mdEvt.target === $sliderHandle[0] ? mdEvt.offsetY : 0; - sliding = true; - - startZooming(); - $sliderHandle.addClass("active"); - - var lastMove = 0; - windowBind('mousemove', sliderMmoveHandler = function( mmEvt ){ - var now = +new Date; - - // throttle the zooms every 10 ms so we don't call zoom too often and cause lag - if( now > lastMove + 10 ){ - lastMove = now; - } else { - return false; - } - - setSliderFromMouse(mmEvt, handleOffset); - - return false; - }); - - // unbind when - windowBind('mouseup', function(){ - windowUnbind('mousemove', sliderMmoveHandler); - sliding = false; - - $sliderHandle.removeClass("active"); - endZooming(); - }); - - return false; - }); - - $slider.bind('mousedown', function(e){ - if( e.target !== $sliderHandle[0] ){ - sliderMdownHandler(e); - setSliderFromMouse(e); - } - }); - - function positionSliderFromZoom(){ - var cy = $container.cytoscape("get"); - var z = cy.zoom(); - var zmin = options.minZoom; - var zmax = options.maxZoom; - - // assume (zoom = zmax ^ p) where p ranges on (x, 1) with x negative - var x = Math.log(zmin) / Math.log(zmax); - var p = Math.log(z) / Math.log(zmax); - var percent = 1 - (p - x) / (1 - x); // the 1- bit at the front b/c up is in the -ve y direction - - var min = sliderPadding; - var max = $slider.height() - $sliderHandle.height() - 2*sliderPadding; - var top = percent * ( max - min ); - - // constrain to slider bounds - if( top < min ){ top = min } - if( top > max ){ top = max } - - // move the handle - $sliderHandle.css('top', top); - } - - positionSliderFromZoom(); - - cyOn('zoom', function(){ - if( !sliding ){ - positionSliderFromZoom(); - } - }); - - // set the position of the zoom=1 tick - (function(){ - var z = 1; - var zmin = options.minZoom; - var zmax = options.maxZoom; - - // assume (zoom = zmax ^ p) where p ranges on (x, 1) with x negative - var x = Math.log(zmin) / Math.log(zmax); - var p = Math.log(z) / Math.log(zmax); - var percent = 1 - (p - x) / (1 - x); // the 1- bit at the front b/c up is in the -ve y direction - - if( percent > 1 || percent < 0 ){ - $noZoomTick.hide(); - return; - } - - var min = sliderPadding; - var max = $slider.height() - $sliderHandle.height() - 2*sliderPadding; - var top = percent * ( max - min ); - - // constrain to slider bounds - if( top < min ){ top = min } - if( top > max ){ top = max } - - $noZoomTick.css('top', top); - })(); - - // set up zoom in/out buttons - ///////////////////////////// - - function bindButton($button, factor){ - var zoomInterval; - - $button.bind("mousedown", function(e){ - e.preventDefault(); - e.stopPropagation(); - - if( e.button != 0 ){ - return; - } - - var cy = $container.cytoscape("get"); - var doZoom = function(){ - var zoom = cy.zoom(); - var lvl = cy.zoom() * factor; - - if( lvl < options.minZoom ){ - lvl = options.minZoom; - } - - if( lvl > options.maxZoom ){ - lvl = options.maxZoom; - } - - if( (lvl == options.maxZoom && zoom == options.maxZoom) || - (lvl == options.minZoom && zoom == options.minZoom) - ){ - return; - } - - zoomTo(lvl); - }; - - startZooming(); - doZoom(); - zoomInterval = setInterval(doZoom, options.zoomDelay); - - return false; - }); - - windowBind("mouseup blur", function(){ - clearInterval(zoomInterval); - endZooming(); - }); - } - - bindButton( $zoomIn, (1 + options.zoomFactor) ); - bindButton( $zoomOut, (1 - options.zoomFactor) ); - - $reset.bind("mousedown", function(e){ - if( e.button != 0 ){ - return; - } - - var cy = $container.cytoscape("get"); - - if( cy.elements().size() === 0 ){ - cy.reset(); - } else { - cy.fit( options.fitPadding ); - } - - return false; - }); - - - - }); - } - }; - - if( functions[fn] ){ - return functions[fn].apply(this, Array.prototype.slice.call( arguments, 1 )); - } else if( typeof fn == 'object' || !fn ) { - return functions.init.apply( this, arguments ); - } else { - $.error("No such function `"+ fn +"` for jquery.cytoscapePanzoom"); - } - - return $(this); - }; - - - if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module - module.exports = register; - } - - if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module - define('cytoscape-panzoom', function(){ - return register; - }); - } - - if( typeof cytoscape !== 'undefined' ){ // expose to global cytoscape (i.e. window.cytoscape) - register( cytoscape, jQuery || {} ); - } - -})(); diff --git a/docs/htmldoc/js/cytoscape-qtip.js b/docs/htmldoc/js/cytoscape-qtip.js deleted file mode 100644 index 43ad8b9..0000000 --- a/docs/htmldoc/js/cytoscape-qtip.js +++ /dev/null @@ -1,362 +0,0 @@ -;(function( $, $$ ){ 'use strict'; - - var isObject = function(o){ - return o != null && typeof o === 'object'; - }; - - var isFunction = function(o){ - return o != null && typeof o === 'function'; - }; - - var isNumber = function(o){ - return o != null && typeof o === 'number'; - }; - - var throttle = function(func, wait, options) { - var leading = true, - trailing = true; - - if (options === false) { - leading = false; - } else if (isObject(options)) { - leading = 'leading' in options ? options.leading : leading; - trailing = 'trailing' in options ? options.trailing : trailing; - } - options = options || {}; - options.leading = leading; - options.maxWait = wait; - options.trailing = trailing; - - return debounce(func, wait, options); - }; - - var debounce = function(func, wait, options) { // ported lodash debounce function - var args, - maxTimeoutId, - result, - stamp, - thisArg, - timeoutId, - trailingCall, - lastCalled = 0, - maxWait = false, - trailing = true; - - if (!isFunction(func)) { - return; - } - wait = Math.max(0, wait) || 0; - if (options === true) { - var leading = true; - trailing = false; - } else if (isObject(options)) { - leading = options.leading; - maxWait = 'maxWait' in options && (Math.max(wait, options.maxWait) || 0); - trailing = 'trailing' in options ? options.trailing : trailing; - } - var delayed = function() { - var remaining = wait - (Date.now() - stamp); - if (remaining <= 0) { - if (maxTimeoutId) { - clearTimeout(maxTimeoutId); - } - var isCalled = trailingCall; - maxTimeoutId = timeoutId = trailingCall = undefined; - if (isCalled) { - lastCalled = Date.now(); - result = func.apply(thisArg, args); - if (!timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - } - } else { - timeoutId = setTimeout(delayed, remaining); - } - }; - - var maxDelayed = function() { - if (timeoutId) { - clearTimeout(timeoutId); - } - maxTimeoutId = timeoutId = trailingCall = undefined; - if (trailing || (maxWait !== wait)) { - lastCalled = Date.now(); - result = func.apply(thisArg, args); - if (!timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - } - }; - - return function() { - args = arguments; - stamp = Date.now(); - thisArg = this; - trailingCall = trailing && (timeoutId || !leading); - - if (maxWait === false) { - var leadingCall = leading && !timeoutId; - } else { - if (!maxTimeoutId && !leading) { - lastCalled = stamp; - } - var remaining = maxWait - (stamp - lastCalled), - isCalled = remaining <= 0; - - if (isCalled) { - if (maxTimeoutId) { - maxTimeoutId = clearTimeout(maxTimeoutId); - } - lastCalled = stamp; - result = func.apply(thisArg, args); - } - else if (!maxTimeoutId) { - maxTimeoutId = setTimeout(maxDelayed, remaining); - } - } - if (isCalled && timeoutId) { - timeoutId = clearTimeout(timeoutId); - } - else if (!timeoutId && wait !== maxWait) { - timeoutId = setTimeout(delayed, wait); - } - if (leadingCall) { - isCalled = true; - result = func.apply(thisArg, args); - } - if (isCalled && !timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - return result; - }; - }; - - function register( $$, $ ){ - - // use a single dummy dom ele as target for every qtip - var $qtipContainer = $('
'); - var viewportDebounceRate = 250; - - function generateOpts( target, passedOpts ){ - var qtip = target.scratch().qtip; - var opts = $.extend( {}, passedOpts ); - - if( !opts.id ){ - opts.id = 'cy-qtip-target-' + ( Date.now() + Math.round( Math.random() * 10000) ); - } - - if( !qtip.$domEle ){ - qtip.$domEle = $qtipContainer; - } - - // qtip should be positioned relative to cy dom container - opts.position = opts.position || {}; - opts.position.container = opts.position.container || $( document.body ); - opts.position.viewport = opts.position.viewport || $( document.body ); - opts.position.target = [0, 0]; - opts.position.my = opts.position.my || 'top center'; - opts.position.at = opts.position.at || 'bottom center'; - - // adjust - var adjust = opts.position.adjust = opts.position.adjust || {}; - adjust.method = adjust.method || 'flip'; - adjust.mouse = false; - - if( adjust.cyAdjustToEleBB === undefined ){ - adjust.cyAdjustToEleBB = true; - } - - // default show event - opts.show = opts.show || {}; - - if( !opts.show.event ){ - opts.show.event = 'tap'; - } - - // default hide event - opts.hide = opts.hide || {}; - opts.hide.cyViewport = opts.hide.cyViewport === undefined ? true : opts.hide.cyViewport; - - if( !opts.hide.event ){ - opts.hide.event = 'unfocus'; - } - - // so multiple qtips can exist at once (only works on recent qtip2 versions) - opts.overwrite = false; - - var content; - if( opts.content ){ - if( isFunction(opts.content) ){ - content = opts.content; - } else if( opts.content.text && isFunction(opts.content.text) ){ - content = opts.content.text; - } - - if( content ){ - opts.content = function(event, api){ - return content.apply( target, [event, api] ); - }; - } - } - - return opts; - } - - $$('collection', 'qtip', function( passedOpts ){ - var eles = this; - var cy = this.cy(); - var container = cy.container(); - - if( passedOpts === 'api' ){ - return this.scratch().qtip.api; - } - - eles.each(function(i, ele){ - var scratch = ele.scratch(); - var qtip = scratch.qtip = scratch.qtip || {}; - var opts = generateOpts( ele, passedOpts ); - var adjNums = opts.position.adjust; - - - qtip.$domEle.qtip( opts ); - var qtipApi = qtip.api = qtip.$domEle.qtip('api'); // save api ref - qtip.$domEle.removeData('qtip'); // remove qtip dom/api ref to be safe - - var updatePosition = function(e){ - var cOff = container.getBoundingClientRect(); - var pos = ele.renderedPosition() || ( e ? e.cyRenderedPosition : undefined ); - if( !pos || pos.x == null || isNaN(pos.x) ){ return; } - - if( opts.position.adjust.cyAdjustToEleBB && ele.isNode() ){ - var my = opts.position.my.toLowerCase(); - var at = opts.position.at.toLowerCase(); - var z = cy.zoom(); - var w = ele.outerWidth() * z; - var h = ele.outerHeight() * z; - - if( at.match('top') ){ - pos.y -= h/2; - } else if( at.match('bottom') ){ - pos.y += h/2; - } - - if( at.match('left') ){ - pos.x -= w/2; - } else if( at.match('right') ){ - pos.x += w/2; - } - - if( isNumber(adjNums.x) ){ - pos.x += adjNums.x; - } - - if( isNumber(adjNums.y) ){ - pos.y += adjNums.y; - } - } - - qtipApi.set('position.adjust.x', cOff.left + pos.x + window.pageXOffset); - qtipApi.set('position.adjust.y', cOff.top + pos.y + window.pageYOffset); - }; - updatePosition(); - - ele.on( opts.show.event, function(e){ - updatePosition(e); - - qtipApi.show(); - } ); - - ele.on( opts.hide.event, function(e){ - qtipApi.hide(); - } ); - - if( opts.hide.cyViewport ){ - cy.on('viewport', debounce(function(){ - qtipApi.hide(); - }, viewportDebounceRate, { leading: true }) ); - } - - if( opts.position.adjust.cyViewport ){ - cy.on('pan zoom', debounce(function(e){ - updatePosition(e); - - qtipApi.reposition(); - }, viewportDebounceRate, { trailing: true }) ); - } - - }); - - return this; // chainability - - }); - - $$('core', 'qtip', function( passedOpts ){ - var cy = this; - var container = cy.container(); - - if( passedOpts === 'api' ){ - return this.scratch().qtip.api; - } - - var scratch = cy.scratch(); - var qtip = scratch.qtip = scratch.qtip || {}; - var opts = generateOpts( cy, passedOpts ); - - - qtip.$domEle.qtip( opts ); - var qtipApi = qtip.api = qtip.$domEle.qtip('api'); // save api ref - qtip.$domEle.removeData('qtip'); // remove qtip dom/api ref to be safe - - var updatePosition = function(e){ - var cOff = container.getBoundingClientRect(); - var pos = e.cyRenderedPosition; - if( !pos || pos.x == null || isNaN(pos.x) ){ return; } - - qtipApi.set('position.adjust.x', cOff.left + pos.x + window.pageXOffset); - qtipApi.set('position.adjust.y', cOff.top + pos.y + window.pageYOffset); - }; - - cy.on( opts.show.event, function(e){ - if( !opts.show.cyBgOnly || (opts.show.cyBgOnly && e.cyTarget === cy) ){ - updatePosition(e); - - qtipApi.show(); - } - } ); - - cy.on( opts.hide.event, function(e){ - if( !opts.hide.cyBgOnly || (opts.hide.cyBgOnly && e.cyTarget === cy) ){ - qtipApi.hide(); - } - } ); - - if( opts.hide.cyViewport ){ - cy.on('viewport', debounce(function(){ - qtipApi.hide(); - }, viewportDebounceRate, { leading: true }) ); - } - - return this; // chainability - - }); - - } - - if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module - module.exports = register; - } - - if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module - define('cytoscape-qtip', function(){ - return register; - }); - } - - if( $ && $$ ){ - register( $$, $ ); - } - -})( - typeof jQuery !== 'undefined' ? jQuery : null, - typeof cytoscape !== 'undefined' ? cytoscape : null -); diff --git a/docs/htmldoc/js/cytoscape.js b/docs/htmldoc/js/cytoscape.js deleted file mode 100644 index 601e6db..0000000 --- a/docs/htmldoc/js/cytoscape.js +++ /dev/null @@ -1,24515 +0,0 @@ -/*! - * This file is part of Cytoscape.js 2.5.1. - * - * Cytoscape.js is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * Cytoscape.js is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * Cytoscape.js. If not, see . - */ - -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.cytoscape = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { - var minPos = findMin(openSet, fScore); - var cMin = cy.getElementById( openSet[minPos] ); - steps++; - - // If we've found our goal, then we are done - if (cMin.id() == target.id()) { - var rPath = reconstructPath(source.id(), target.id(), cameFrom, []); - rPath.reverse(); - return { - found : true, - distance : gScore[cMin.id()], - path : eles.spawn(rPath), - steps : steps - }; - } - - // Add cMin to processed nodes - closedSet.push(cMin.id()); - // 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.connectedEdges(); - if( directed ){ vwEdges = vwEdges.stdFilter(function(ele){ return ele.data('source') === cMin.id(); }); } - vwEdges = vwEdges.intersect(edges); - - for (var i = 0; i < vwEdges.length; i++) { - var e = vwEdges[i]; - var w = e.connectedNodes().stdFilter(function(n){ return n.id() !== cMin.id(); }).intersect(nodes); - - // if node is in closedSet, ignore it - if (closedSet.indexOf(w.id()) != -1) { - continue; - } - - // New tentative score for node w - var tempScore = gScore[cMin.id()] + weightFn.apply(e, [e]); - - // Update gScore for node w if: - // w not present in openSet - // OR - // tentative gScore is less than previous value - - // w not in openSet - if (openSet.indexOf(w.id()) == -1) { - gScore[w.id()] = tempScore; - fScore[w.id()] = tempScore + heuristic(w); - openSet.push(w.id()); // Add node to openSet - cameFrom[w.id()] = cMin.id(); - cameFromEdge[w.id()] = e.id(); - continue; - } - // w already in openSet, but with greater gScore - if (tempScore < gScore[w.id()]) { - gScore[w.id()] = tempScore; - fScore[w.id()] = tempScore + heuristic(w); - cameFrom[w.id()] = cMin.id(); - } - - } // End of neighbors update - - } // End of main loop - - // If we've reached here, then we've not reached our goal - return { - found : false, - distance : undefined, - path : undefined, - steps : steps - }; - } - -}); // elesfn - - -module.exports = elesfn; - -},{"../../is":77}],3:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../is'); -var util = _dereq_('../../util'); - -var elesfn = ({ - - // Implemented from pseudocode from wikipedia - bellmanFord: function(options) { - var eles = this; - - options = options || {}; - - // 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(e) {return 1;}; - } - - // directed - optional - if (options.directed != null) { - var directed = options.directed; - } else { - var directed = false; - } - - // 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; - - // mapping: node id -> position in nodes array - var id2position = {}; - for (var i = 0; i < numNodes; i++) { - id2position[nodes[i].id()] = i; - } - - // Initializations - var cost = []; - var predecessor = []; - var predEdge = []; - - for (var i = 0; i < numNodes; i++) { - if (nodes[i].id() === source.id()) { - cost[i] = 0; - } else { - cost[i] = Infinity; - } - predecessor[i] = undefined; - } - - // 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.apply(edges[e], [edges[e]]); - - var temp = cost[sourceIndex] + weight; - if (temp < cost[targetIndex]) { - cost[targetIndex] = temp; - predecessor[targetIndex] = sourceIndex; - predEdge[targetIndex] = edges[e]; - flag = 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; - } - } - } - - if (!flag) { - break; - } - } - - 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.apply(edges[e], [edges[e]]); - - if (cost[sourceIndex] + weight < cost[targetIndex]) { - util.error("Graph contains a negative weight cycle for Bellman-Ford"); - return { pathTo: undefined, - distanceTo: undefined, - hasNegativeWeightCycle: true}; - } - } - } - - // Build result object - var position2id = []; - for (var i = 0; i < numNodes; i++) { - position2id.push(nodes[i].id()); - } - - - var res = { - distanceTo : function(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(); - } - - return cost[id2position[toId]]; - }, - - pathTo : function(to) { - - var reconstructPathAux = function(predecessor, fromPos, toPos, position2id, acumPath, predEdge) { - for(;;){ - // Add toId to path - acumPath.push( cy.getElementById(position2id[toPos]) ); - acumPath.push( predEdge[toPos] ); - - if (fromPos === toPos) { - // reached starting node - return acumPath; - } - - // If no path exists, discart acumulated path and return undefined - var predPos = predecessor[toPos]; - if (typeof predPos === "undefined") { - return undefined; - } - - toPos = predPos; - } - - }; - - 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 = []; - - // This returns a reversed path - var res = reconstructPathAux(predecessor, - id2position[source.id()], - id2position[toId], - position2id, - path, - predEdge); - - // Get it in the correct order and return it - if (res != null) { - res.reverse(); - } - - return eles.spawn(res); - }, - - hasNegativeWeightCycle: false - }; - - return res; - - } // bellmanFord - -}); // elesfn - -module.exports = elesfn; - -},{"../../is":77,"../../util":94}],4:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../is'); - -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 (options) { - options = options || {}; - - // Weight - optional - if (options.weight != null && is.fn(options.weight)) { - var weightFn = options.weight; - var weighted = true; - } else { - var weighted = false; - } - - // Directed - default false - if (options.directed != null && is.bool(options.directed)) { - var directed = options.directed; - } else { - var directed = false; - } - - var priorityInsert = function (queue, ele) { - queue.unshift(ele); - for (var i = 0; d[queue[i]] < d[queue[i + 1]] && i < queue.length - 1; i++) { - var tmp = queue[i]; - queue[i] = queue[i + 1]; - queue[i + 1] = tmp; - } - }; - - var cy = this._private.cy; - - // starting - var V = this.nodes(); - var A = {}; - var C = {}; - - // A contains the neighborhoods of every node - for (var i = 0; i < V.length; i++) { - if (directed) { - A[V[i].id()] = V[i].outgoers("node"); // get outgoers of every node - } else { - A[V[i].id()] = V[i].openNeighborhood("node"); // get neighbors of every node - } - } - - // C contains the betweenness values - for (var i = 0; i < V.length; i++) { - C[V[i].id()] = 0; - } - - for (var s = 0; s < V.length; s++) { - var S = []; // stack - var P = {}; - var g = {}; - var d = {}; - var Q = []; // queue - - // init dictionaries - for (var i = 0; i < V.length; i++) { - P[V[i].id()] = []; - g[V[i].id()] = 0; - d[V[i].id()] = Number.POSITIVE_INFINITY; - } - - g[V[s].id()] = 1; // sigma - d[V[s].id()] = 0; // distance to s - - Q.unshift(V[s].id()); - - while (Q.length > 0) { - var v = Q.pop(); - S.push(v); - if (weighted) { - A[v].forEach(function (w) { - if (cy.$('#' + v).edgesTo(w).length > 0) { - var edge = cy.$('#' + v).edgesTo(w)[0]; - } else { - var edge = w.edgesTo('#' + v)[0]; - } - - var edgeWeight = weightFn.apply(edge, [edge]); - - if (d[w.id()] > d[v] + edgeWeight) { - d[w.id()] = d[v] + edgeWeight; - if (Q.indexOf(w.id()) < 0) { //if w is not in Q - priorityInsert(Q, w.id()); - } else { // update position if w is in Q - Q.splice(Q.indexOf(w.id()), 1); - priorityInsert(Q, w.id()); - } - g[w.id()] = 0; - P[w.id()] = []; - } - if (d[w.id()] == d[v] + edgeWeight) { - g[w.id()] = g[w.id()] + g[v]; - P[w.id()].push(v); - } - }); - } else { - A[v].forEach(function (w) { - if (d[w.id()] == Number.POSITIVE_INFINITY) { - Q.unshift(w.id()); - d[w.id()] = d[v] + 1; - } - if (d[w.id()] == d[v] + 1) { - g[w.id()] = g[w.id()] + g[v]; - P[w.id()].push(v); - } - }); - } - } - - var e = {}; - for (var i = 0; i < V.length; i++) { - e[V[i].id()] = 0; - } - - while (S.length > 0) { - var w = S.pop(); - P[w].forEach(function (v) { - e[v] = e[v] + (g[v] / g[w]) * (1 + e[w]); - if (w != V[s].id()) - C[w] = C[w] + e[w]; - }); - } - } - - var max = 0; - for (var key in C) { - if (max < C[key]) - max = C[key]; - } - - var ret = { - betweenness: function (node) { - if (is.string(node)) { - var node = (cy.filter(node)[0]).id(); - } else { - var node = node.id(); - } - - return C[node]; - }, - - betweennessNormalized: function (node) { - if (is.string(node)) { - var node = (cy.filter(node)[0]).id(); - } else { - var node = node.id(); - } - - return C[node] / max; - } - }; - - // alias - ret.betweennessNormalised = ret.betweennessNormalized; - - return ret; - } // betweennessCentrality - -}); // elesfn - -// nice, short mathemathical alias -elesfn.bc = elesfn.betweennessCentrality; - -module.exports = elesfn; - -},{"../../is":77}],5:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../is'); -var Heap = _dereq_('../../heap'); - -var defineSearch = function( params ){ - params = { - bfs: params.bfs || !params.dfs, - dfs: params.dfs || !params.bfs - }; - - // from pseudocode on wikipedia - return function searchFn( roots, fn, directed ){ - var options; - var std; - var thisArg; - if( is.plainObject(roots) && !is.elementOrCollection(roots) ){ - options = roots; - roots = options.roots || options.root; - fn = options.visit; - directed = options.directed; - std = options.std; - thisArg = options.thisArg; - } - - directed = arguments.length === 2 && !is.fn(fn) ? fn : directed; - fn = is.fn(fn) ? fn : function(){}; - - 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(); - - // enqueue v - for( var i = 0; i < v.length; i++ ){ - if( v[i].isNode() ){ - Q.unshift( v[i] ); - - if( params.bfs ){ - V[ v[i].id() ] = true; - - connectedNodes.push( v[i] ); - } - - id2depth[ v[i].id() ] = 0; - } - } - - while( Q.length !== 0 ){ - var v = params.bfs ? Q.shift() : Q.pop(); - - if( params.dfs ){ - if( V[ v.id() ] ){ continue; } - - V[ v.id() ] = true; - - connectedNodes.push( v ); - } - - var depth = id2depth[ v.id() ]; - var prevEdge = connectedBy[ v.id() ]; - var prevNode = prevEdge == null ? undefined : prevEdge.connectedNodes().not( v )[0]; - var ret; - - if( std ){ - ret = fn.call(thisArg, v, prevEdge, prevNode, j++, depth); - } else { - ret = fn.call(v, j++, depth, v, prevEdge, prevNode); - } - - if( ret === true ){ - found = v; - break; - } - - if( ret === false ){ - break; - } - - var vwEdges = v.connectedEdges(directed ? function(){ return this.data('source') === v.id(); } : undefined).intersect( edges ); - for( var i = 0; i < vwEdges.length; i++ ){ - var e = vwEdges[i]; - var w = e.connectedNodes(function(){ return this.id() !== v.id(); }).intersect( nodes ); - - if( w.length !== 0 && !V[ w.id() ] ){ - w = w[0]; - - Q.push( w ); - - if( params.bfs ){ - V[ w.id() ] = true; - - connectedNodes.push( w ); - } - - connectedBy[ w.id() ] = e; - - id2depth[ w.id() ] = id2depth[ v.id() ] + 1; - } - } - - } - - var connectedEles = []; - - for( var i = 0; i < connectedNodes.length; i++ ){ - var node = connectedNodes[i]; - var edge = connectedBy[ node.id() ]; - - if( edge ){ - connectedEles.push( edge ); - } - - connectedEles.push( node ); - } - - return { - path: cy.collection( connectedEles, { unique: true } ), - found: cy.collection( found ) - }; - }; -}; - -// search, spanning trees, etc -var elesfn = ({ - - breadthFirstSearch: defineSearch({ bfs: true }), - depthFirstSearch: defineSearch({ dfs: true }), - - // kruskal's algorithm (finds min spanning tree, assuming undirected graph) - // implemented from pseudocode from wikipedia - kruskal: function( 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 A = cy.collection(cy, []); - var forest = []; - var nodes = this.nodes(); - - for( var i = 0; i < nodes.length; i++ ){ - forest.push( nodes[i].collection() ); - } - - var edges = this.edges(); - var S = edges.toArray().sort(function(a, b){ - var weightA = weightFn.call(a, a); - var weightB = weightFn.call(b, b); - - return weightA - weightB; - }); - - 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); - - if( setU.index !== setV.index ){ - A = A.add( edge ); - - // combine forests for u and v - forest[ setU.index ] = setU.eles.add( setV.eles ); - forest.splice( setV.index, 1 ); - } - } - - return nodes.add( A ); - - }, - - dijkstra: function( root, weightFn, directed ){ - var options; - if( is.plainObject(root) && !is.elementOrCollection(root) ){ - options = root; - root = options.root; - weightFn = options.weight; - directed = options.directed; - } - - var cy = this._private.cy; - weightFn = is.fn(weightFn) ? weightFn : function(){ return 1; }; // if not specified, assume each edge has equal weight (1) - - var source = is.string(root) ? this.filter(root)[0] : root[0]; - var dist = {}; - var prev = {}; - var knownDist = {}; - - var edges = this.edges().filter(function(){ return !this.isLoop(); }); - var nodes = this.nodes(); - - var getDist = function(node){ - return dist[ node.id() ]; - }; - - var setDist = function(node, d){ - dist[ node.id() ] = d; - - Q.updateItem( node ); - }; - - 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(u, v){ - var uvs = ( directed ? u.edgesTo(v) : u.edgesWith(v) ).intersect(edges); - var smallestDistance = Infinity; - var smallestEdge; - - for( var i = 0; i < uvs.length; i++ ){ - var edge = uvs[i]; - var weight = weightFn.apply( edge, [edge] ); - - if( weight < smallestDistance || !smallestEdge ){ - smallestDistance = weight; - smallestEdge = edge; - } - } - - return { - edge: smallestEdge, - dist: smallestDistance - }; - }; - - while( Q.size() > 0 ){ - var u = Q.pop(); - var smalletsDist = getDist(u); - var uid = u.id(); - - knownDist[uid] = smalletsDist; - - if( smalletsDist === Math.Infinite ){ - break; - } - - 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 alt = smalletsDist + vDist.dist; - - if( alt < getDist(v) ){ - setDist(v, alt); - - prev[ vid ] = { - node: u, - edge: vDist.edge - }; - } - } // for - } // while - - return { - distanceTo: function(node){ - var target = is.string(node) ? nodes.filter(node)[0] : node[0]; - - return knownDist[ target.id() ]; - }, - - pathTo: function(node){ - var target = is.string(node) ? nodes.filter(node)[0] : node[0]; - var S = []; - var u = target; - - if( target.length > 0 ){ - S.unshift( target ); - - while( prev[ u.id() ] ){ - var p = prev[ u.id() ]; - - S.unshift( p.edge ); - S.unshift( p.node ); - - u = p.node; - } - } - - return cy.collection( S ); - } - }; - } -}); - -// nice, short mathemathical alias -elesfn.bfs = elesfn.breadthFirstSearch; -elesfn.dfs = elesfn.depthFirstSearch; - -module.exports = elesfn; - -},{"../../heap":75,"../../is":77}],6:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../is'); - -var elesfn = ({ - - closenessCentralityNormalized: function (options) { - options = options || {}; - - var cy = this.cy(); - - var harmonic = options.harmonic; - if( harmonic === undefined ){ - harmonic = true; - } - - var closenesses = {}; - var maxCloseness = 0; - var nodes = this.nodes(); - var fw = this.floydWarshall({ weight: options.weight, directed: options.directed }); - - // 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; - } - - return { - closeness: function (node) { - 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 (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; - } - - // weight - optional - if (options.weight != null && is.fn(options.weight)) { - var weight = options.weight; - } else { - var weight = function(){return 1;}; - } - - // directed - optional - if (options.directed != null && is.bool(options.directed)) { - var directed = options.directed; - } else { - var directed = false; - } - - var harmonic = options.harmonic; - if( harmonic === undefined ){ - harmonic = true; - } - - // we need distance from this node to every other node - var dijkstra = this.dijkstra({ - root: root, - weight: weight, - directed: directed - }); - var totalDistance = 0; - - 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 harmonic ? totalDistance : 1 / totalDistance; - } // closenessCentrality - -}); // elesfn - -// nice, short mathemathical alias -elesfn.cc = elesfn.closenessCentrality; -elesfn.ccn = elesfn.closenessCentralityNormalised = elesfn.closenessCentralityNormalized; - -module.exports = elesfn; - -},{"../../is":77}],7:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../is'); -var util = _dereq_('../../util'); - -var elesfn = ({ - - degreeCentralityNormalized: function (options) { - options = options || {}; - - var cy = this.cy(); - - // directed - optional - if (options.directed != null) { - var directed = options.directed; - } else { - 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; - } - - return { - degree: function (node) { - 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; - - 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 (maxIndegree < currDegree.indegree) - maxIndegree = currDegree.indegree; - - if (maxOutdegree < currDegree.outdegree) - maxOutdegree = currDegree.outdegree; - - indegrees[node.id()] = currDegree.indegree; - outdegrees[node.id()] = currDegree.outdegree; - } - - return { - indegree: function (node) { - 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 indegrees[node] / maxIndegree; - }, - outdegree: function (node) { - 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 outdegrees[node] / maxOutdegree; - } - - }; - } - - }, // 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 (options) { - options = options || {}; - - var callingEles = this; - - // 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; - } - - // 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 (e) { - return 1; - }; - } - - // directed - optional - if (options.directed != null) { - var directed = options.directed; - } else { - var directed = false; - } - - // alpha - optional - if (options.alpha != null && is.number(options.alpha)) { - var alpha = options.alpha; - } else { - alpha = 0; - } - - - if (!directed) { - var connEdges = root.connectedEdges().intersection( callingEles ); - var k = connEdges.length; - var s = 0; - - // Now, sum edge weights - for (var i = 0; i < connEdges.length; i++) { - var edge = connEdges[i]; - s += weightFn.apply(edge, [edge]); - } - - 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; - - // Now, sum incoming edge weights - for (var i = 0; i < incoming.length; i++) { - var edge = incoming[i]; - s_in += weightFn.apply(edge, [edge]); - } - - // Now, sum outgoing edge weights - for (var i = 0; i < outgoing.length; i++) { - var edge = outgoing[i]; - s_out += weightFn.apply(edge, [edge]); - } - - 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 - -}); // elesfn - -// nice, short mathemathical alias -elesfn.dc = elesfn.degreeCentrality; -elesfn.dcn = elesfn.degreeCentralityNormalised = elesfn.degreeCentralityNormalized; - -module.exports = elesfn; - -},{"../../is":77,"../../util":94}],8:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../is'); - -var elesfn = ({ - - // Implemented from pseudocode from wikipedia - floydWarshall: function(options) { - 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(e) {return 1;}; - } - - // 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 numNodes = nodes.length; - - // mapping: node id -> position in nodes array - var id2position = {}; - for (var i = 0; i < numNodes; i++) { - id2position[nodes[i].id()] = i; - } - - // 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); - } - - // Initialize matrix used for path reconstruction - // Initialize distance matrix - var next = []; - var edgeNext = []; - - var initMatrix = function(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); - - // 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.apply(edges[i], [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]; - } - } - - // 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.apply(edges[i], [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]; - } - } - } - - // 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]; - } - } - } - } - - // Build result object - var position2id = []; - for (var i = 0; i < numNodes; i++) { - position2id.push(nodes[i].id()); - } - - var res = { - distance: function(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(); - } - - 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(); - } - - return dist[id2position[fromId]][id2position[toId]]; - }, - - path: function(from, to) { - var reconstructPathAux = function(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 ); - - path.push( cy.getElementById(position2id[from]) ); - } - return path; - }; - - 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 (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 pathArr = reconstructPathAux(id2position[fromId], - id2position[toId], - next, - position2id, - edgeNext); - - return cy.collection( pathArr ); - }, - }; - - return res; - - } // floydWarshall - -}); // elesfn - -module.exports = elesfn; - -},{"../../is":77}],9:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); - -var elesfn = {}; - -[ - _dereq_('./bfs-dfs'), - _dereq_('./a-star'), - _dereq_('./floyd-warshall'), - _dereq_('./bellman-ford'), - _dereq_('./kerger-stein'), - _dereq_('./page-rank'), - _dereq_('./degree-centrality'), - _dereq_('./closeness-centrality'), - _dereq_('./betweenness-centrality') -].forEach(function( props ){ - util.extend( elesfn, props ); -}); - -module.exports = elesfn; - -},{"../../util":94,"./a-star":2,"./bellman-ford":3,"./betweenness-centrality":4,"./bfs-dfs":5,"./closeness-centrality":6,"./degree-centrality":7,"./floyd-warshall":8,"./kerger-stein":10,"./page-rank":11}],10:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); - -var elesfn = ({ - - // Computes the minimum cut of an undirected graph - // Returns the correct answer with high probability - kargerStein: function(options) { - var eles = this; - - options = options || {}; - - // 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(edgeIndex, nodeMap, remainingEdges) { - var edgeInfo = remainingEdges[edgeIndex]; - var sourceIn = edgeInfo[1]; - var targetIn = edgeInfo[2]; - var partition1 = nodeMap[sourceIn]; - var partition2 = nodeMap[targetIn]; - - // 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; - }); - - // 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; - } - } - - // Move all nodes from partition2 to partition1 - for (var i = 0; i < nodeMap.length; i++) { - if (nodeMap[i] === partition2) { - nodeMap[i] = partition1; - } - } - - return newEdges; - }; - - - // Contracts a graph until we reach a certain number of meta nodes - var contractUntil = function(metaNodeMap, - remainingEdges, - size, - sizeLimit) { - // Stop condition - if (size <= sizeLimit) { - return remainingEdges; - } - - // Choose an edge randomly - var edgeIndex = Math.floor((Math.random() * remainingEdges.length)); - - // Colapse graph based on edge - var newEdges = colapse(edgeIndex, metaNodeMap, remainingEdges); - - return contractUntil(metaNodeMap, - newEdges, - size - 1, - sizeLimit); - }; - - 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 (numNodes < 2) { - util.error("At least 2 nodes are required for Karger-Stein algorithm"); - return undefined; - } - - // 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; - } - - // 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()]]); - } - - // We will store the best cut found here - var minCutSize = Infinity; - var minCut; - - // Initial meta node partition - var originalMetaNode = []; - for (var i = 0; i < numNodes; i++) { - originalMetaNode.push(i); - } - - // Main loop - for (var iter = 0; iter <= numIter; iter++) { - // Create new meta node partition - var metaNodeMap = originalMetaNode.slice(0); - - // Contract until stop point (stopSize nodes) - var edgesState = contractUntil(metaNodeMap, edgeIndexes, numNodes, stopSize); - - // Create a copy of the colapsed nodes state - var metaNodeMap2 = metaNodeMap.slice(0); - - // Run 2 iterations starting in the stop state - var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2); - var res2 = contractUntil(metaNodeMap2, edgesState, stopSize, 2); - - // 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 - - - // Construct result - var resEdges = (minCut[0]).map(function(e){ return edges[e[0]]; }); - var partition1 = []; - var partition2 = []; - - // 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; - -},{"../../util":94}],11:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../is'); - -var elesfn = ({ - - pageRank: function(options) { - options = options || {}; - - var normalizeVector = function(vector) { - var length = vector.length; - - // 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(e) {return 1;}; - } - - 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; - } - - // 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 = []; - for (var j = 0; j < numNodes; j++) { - newRow.push(0.0); - } - 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.apply(edge, [edge]); - - // Update matrix - matrix[t][s] += w; - - // Update column sum - columnSum[s] += w; - } - - // 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; - } - } - } - - // 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++) { - eigenvector.push(1.0); - nullVector.push(0.0); - } - - for (var iter = 0; iter < numIter; iter++) { - // New array with all 0's - var temp = nullVector.slice(0); - - // 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]; - } - } - - 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); - } - - // If difference is less than the desired threshold, stop iterating - if (diff < epsilon) { - break; - } - } - - // Construct result - var res = { - rank : function(node) { - if (is.string(node)) { - // is a selector string - var nodeId = (cy.filter(node)[0]).id(); - } else { - // is a node object - var nodeId = node.id(); - } - return eigenvector[id2position[nodeId]]; - } - }; - - - return res; - } // pageRank - -}); // elesfn - -module.exports = elesfn; - -},{"../../is":77}],12:[function(_dereq_,module,exports){ -'use strict'; - -var define = _dereq_('../define'); - -var elesfn = ({ - animate: define.animate(), - animation: define.animation(), - animated: define.animated(), - clearQueue: define.clearQueue(), - delay: define.delay(), - delayAnimation: define.delayAnimation(), - stop: define.stop(), -}); - -module.exports = elesfn; - -},{"../define":41}],13:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); - -var elesfn = ({ - classes: function( classes ){ - classes = classes.match(/\S+/g) || []; - var self = this; - var changed = []; - var classesMap = {}; - - // fill in classes map - for( var i = 0; i < classes.length; i++ ){ - var cls = classes[i]; - - classesMap[ cls ] = true; - } - - // check and update each ele - for( var j = 0; j < self.length; 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 - for( var i = 0; i < classes.length; i++ ){ - var cls = classes[i]; - var eleHasClass = eleClasses[ cls ]; - - if( !eleHasClass ){ - changedEle = true; - break; - } - } - - // check if ele has classes outside of those passed - if( !changedEle ){ for( var eleCls in eleClasses ){ - var eleHasClass = eleClasses[ eleCls ]; - var specdClass = classesMap[ eleCls ]; // i.e. this class is passed to the function - - if( eleHasClass && !specdClass ){ - changedEle = true; - break; - } - } } - - if( changedEle ){ - _p.classes = util.copy( classesMap ); - - changed.push( ele ); - } - } - - // trigger update style on those eles that had class changes - if( changed.length > 0 ){ - this.spawn(changed) - .updateStyle() - .trigger('class') - ; - } - - return self; - }, - - addClass: function( classes ){ - return this.toggleClass( classes, true ); - }, - - hasClass: function( className ){ - var ele = this[0]; - return ( ele != null && ele._private.classes[className] ) ? true : false; - }, - - toggleClass: function( classesStr, toggle ){ - var classes = classesStr.match(/\S+/g) || []; - var self = this; - var changed = []; // eles who had classes changed - - for( var i = 0, il = self.length; i < il; i++ ){ - var ele = self[i]; - var changedEle = false; - - for( var j = 0; j < classes.length; j++ ){ - var cls = classes[j]; - var eleClasses = ele._private.classes; - var hasClass = eleClasses[cls]; - var shouldAdd = toggle || (toggle === undefined && !hasClass); - - if( shouldAdd ){ - eleClasses[cls] = true; - - if( !hasClass && !changedEle ){ - changed.push(ele); - changedEle = true; - } - } else { // then remove - eleClasses[cls] = false; - - if( hasClass && !changedEle ){ - changed.push(ele); - changedEle = true; - } - } - - } // for j classes - } // for i eles - - // trigger update style on those eles that had class changes - if( changed.length > 0 ){ - this.spawn(changed) - .updateStyle() - .trigger('class') - ; - } - - return self; - }, - - removeClass: function( classes ){ - return this.toggleClass( classes, false ); - }, - - flashClass: function( classes, duration ){ - var self = this; - - if( duration == null ){ - duration = 250; - } else if( duration === 0 ){ - return self; // nothing to do really - } - - self.addClass( classes ); - setTimeout(function(){ - self.removeClass( classes ); - }, duration); - - return self; - } -}); - -module.exports = elesfn; - -},{"../util":94}],14:[function(_dereq_,module,exports){ -'use strict'; - -var elesfn = ({ - allAre: function( selector ){ - return this.filter(selector).length === this.length; - }, - - is: function( selector ){ - return this.filter(selector).length > 0; - }, - - some: function( 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( ret ){ - return true; - } - } - - return false; - }, - - every: function( 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( !ret ){ - return false; - } - } - - return true; - }, - - same: function( collection ){ - collection = this.cy().collection( collection ); - - // cheap extra check - if( this.length !== collection.length ){ - return false; - } - - return this.intersect( collection ).length === this.length; - }, - - anySame: function( collection ){ - collection = this.cy().collection( collection ); - - return this.intersect( collection ).length > 0; - }, - - allAreNeighbors: function( collection ){ - collection = this.cy().collection( collection ); - - return this.neighborhood().intersect( collection ).length === collection.length; - } -}); - -elesfn.allAreNeighbours = elesfn.allAreNeighbors; - -module.exports = elesfn; - -},{}],15:[function(_dereq_,module,exports){ -'use strict'; - -var elesfn = ({ - parent: function( selector ){ - var parents = []; - var cy = this._private.cy; - - for( var i = 0; i < this.length; i++ ){ - var ele = this[i]; - var parent = cy.getElementById( ele._private.data.parent ); - - if( parent.size() > 0 ){ - parents.push( parent ); - } - } - - return this.spawn( parents, { unique: true } ).filter( selector ); - }, - - parents: function( selector ){ - var parents = []; - - var eles = this.parent(); - while( eles.nonempty() ){ - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - parents.push( ele ); - } - - eles = eles.parent(); - } - - return this.spawn( parents, { unique: true } ).filter( selector ); - }, - - commonAncestors: function( selector ){ - var ancestors; - - for( var i = 0; i < this.length; i++ ){ - var ele = this[i]; - var parents = ele.parents(); - - ancestors = ancestors || parents; - - ancestors = ancestors.intersect( parents ); // current list must be common with current ele parents set - } - - return ancestors.filter( selector ); - }, - - orphans: function( selector ){ - return this.stdFilter(function( ele ){ - return ele.isNode() && ele.parent().empty(); - }).filter( selector ); - }, - - nonorphans: function( selector ){ - return this.stdFilter(function( ele ){ - return ele.isNode() && ele.parent().nonempty(); - }).filter( selector ); - }, - - children: function( selector ){ - var children = []; - - for( var i = 0; i < this.length; i++ ){ - var ele = this[i]; - children = children.concat( ele._private.children ); - } - - return this.spawn( children, { unique: true } ).filter( selector ); - }, - - siblings: function( selector ){ - return this.parent().children().not( this ).filter( selector ); - }, - - isParent: function(){ - var ele = this[0]; - - if( ele ){ - return ele._private.children.length !== 0; - } - }, - - isChild: function(){ - var ele = this[0]; - - if( ele ){ - return ele._private.data.parent !== undefined && ele.parent().length !== 0; - } - }, - - descendants: function( selector ){ - var elements = []; - - function add( eles ){ - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - - elements.push( ele ); - - if( ele.children().nonempty() ){ - add( ele.children() ); - } - } - } - - add( this.children() ); - - return this.spawn( elements, { unique: true } ).filter( selector ); - } -}); - -// aliases -elesfn.ancestors = elesfn.parents; - -module.exports = elesfn; - -},{}],16:[function(_dereq_,module,exports){ -'use strict'; - -var define = _dereq_('../define'); -var fn, elesfn; - -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 - }), - - removeData: define.removeData({ - field: 'data', - event: 'data', - triggerFnName: 'trigger', - triggerEvent: true, - immutableKeys: { - 'id': true, - 'source': true, - 'target': true, - 'parent': true - }, - updateStyle: true - }), - - scratch: define.data({ - field: 'scratch', - bindingEvent: 'scratch', - allowBinding: true, - allowSetting: true, - settingEvent: 'scratch', - settingTriggersEvent: true, - triggerFnName: 'trigger', - allowGetting: true, - updateStyle: true - }), - - removeScratch: define.removeData({ - field: 'scratch', - event: 'scratch', - triggerFnName: 'trigger', - triggerEvent: true, - updateStyle: true - }), - - rscratch: define.data({ - field: 'rscratch', - allowBinding: false, - allowSetting: true, - settingTriggersEvent: false, - allowGetting: true - }), - - removeRscratch: define.removeData({ - field: 'rscratch', - triggerEvent: false - }), - - id: function(){ - var ele = this[0]; - - if( ele ){ - return ele._private.data.id; - } - } - -}); - -// aliases -fn.attr = fn.data; -fn.removeAttr = fn.removeData; - -module.exports = elesfn; - -},{"../define":41}],17:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); - -var elesfn = {}; - -function defineDegreeFunction(callback){ - return function( includeLoops ){ - var self = this; - - if( includeLoops === undefined ){ - includeLoops = true; - } - - if( self.length === 0 ){ return; } - - if( self.isNode() && !self.removed() ){ - var degree = 0; - var node = self[0]; - var connectedEdges = node._private.edges; - - for( var i = 0; i < connectedEdges.length; i++ ){ - var edge = connectedEdges[i]; - - if( !includeLoops && edge.isLoop() ){ - continue; - } - - degree += callback( node, edge ); - } - - return degree; - } else { - return; - } - }; -} - -util.extend(elesfn, { - degree: defineDegreeFunction(function(node, edge){ - if( edge.source().same( edge.target() ) ){ - return 2; - } else { - return 1; - } - }), - - indegree: defineDegreeFunction(function(node, edge){ - if( edge.target().same(node) ){ - return 1; - } else { - return 0; - } - }), - - outdegree: defineDegreeFunction(function(node, edge){ - if( edge.source().same(node) ){ - return 1; - } else { - return 0; - } - }) -}); - -function defineDegreeBoundsFunction(degreeFn, callback){ - return function( includeLoops ){ - var ret; - var nodes = this.nodes(); - - 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; - } - } - - return ret; - }; -} - -util.extend(elesfn, { - minDegree: defineDegreeBoundsFunction('degree', function(degree, min){ - return degree < min; - }), - - maxDegree: defineDegreeBoundsFunction('degree', function(degree, max){ - return degree > max; - }), - - minIndegree: defineDegreeBoundsFunction('indegree', function(degree, min){ - return degree < min; - }), - - maxIndegree: defineDegreeBoundsFunction('indegree', function(degree, max){ - return degree > max; - }), - - minOutdegree: defineDegreeBoundsFunction('outdegree', function(degree, min){ - return degree < min; - }), - - maxOutdegree: defineDegreeBoundsFunction('outdegree', function(degree, max){ - return degree > max; - }) -}); - -util.extend(elesfn, { - totalDegree: function( includeLoops ){ - var total = 0; - var nodes = this.nodes(); - - for( var i = 0; i < nodes.length; i++ ){ - total += nodes[i].degree( includeLoops ); - } - - return total; - } -}); - -module.exports = elesfn; - -},{"../util":94}],18:[function(_dereq_,module,exports){ -'use strict'; - -var define = _dereq_('../define'); -var is = _dereq_('../is'); -var util = _dereq_('../util'); -var fn, elesfn; - -fn = elesfn = ({ - - position: define.data({ - field: 'position', - bindingEvent: 'position', - allowBinding: true, - allowSetting: true, - settingEvent: 'position', - settingTriggersEvent: true, - triggerFnName: 'rtrigger', - allowGetting: true, - validKeys: ['x', 'y'], - onSet: function( eles ){ - var updatedEles = eles.updateCompoundBounds(); - updatedEles.rtrigger('position'); - }, - canSet: function( ele ){ - return !ele.locked() && !ele.isParent(); - } - }), - - // position but no notification to renderer - silentPosition: define.data({ - field: 'position', - bindingEvent: 'position', - allowBinding: false, - allowSetting: true, - settingEvent: 'position', - settingTriggersEvent: false, - triggerFnName: 'trigger', - allowGetting: true, - validKeys: ['x', 'y'], - onSet: function( eles ){ - eles.updateCompoundBounds(); - }, - canSet: function( ele ){ - return !ele.locked() && !ele.isParent(); - } - }), - - positions: function( pos, silent ){ - if( is.plainObject(pos) ){ - this.position(pos); - - } else if( is.fn(pos) ){ - var fn = pos; - - for( var i = 0; i < this.length; i++ ){ - var ele = this[i]; - - var pos = fn.apply(ele, [i, ele]); - - if( pos && !ele.locked() && !ele.isParent() ){ - var elePos = ele._private.position; - elePos.x = pos.x; - elePos.y = pos.y; - } - } - - var updatedEles = this.updateCompoundBounds(); - var toTrigger = updatedEles.length > 0 ? this.add( updatedEles ) : this; - - if( silent ){ - toTrigger.trigger('position'); - } else { - toTrigger.rtrigger('position'); - } - } - - return this; // chaining - }, - - silentPositions: function( pos ){ - return this.positions( pos, true ); - }, - - // get/set the rendered (i.e. on screen) positon of the element - renderedPosition: function( 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( val !== undefined ){ // set one dimension - ele._private.position[dim] = ( val - pan[dim] )/zoom; - } else if( rpos !== undefined ){ // set whole position - ele._private.position = { - x: ( rpos.x - pan.x ) /zoom, - y: ( rpos.y - pan.y ) /zoom - }; - } - } - - this.rtrigger('position'); - } else { // getting - var pos = ele._private.position; - rpos = { - x: pos.x * zoom + pan.x, - y: pos.y * zoom + pan.y - }; - - 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 - } - - return this; // chaining - }, - - // get/set the position relative to the parent - relativePosition: function( 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 && 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]; - var parent = hasCompoundNodes ? ele.parent() : null; - var hasParent = parent && parent.length > 0; - var relativeToParent = hasParent; - - if( hasParent ){ - parent = parent[0]; - } - - var origin = relativeToParent ? parent._private.position : { x: 0, y: 0 }; - - if( val !== undefined ){ // set one dimension - ele._private.position[dim] = val + origin[dim]; - } else if( ppos !== undefined ){ // set whole position - ele._private.position = { - x: ppos.x + origin.x, - y: ppos.y + origin.y, - }; - } - } - - this.rtrigger('position'); - - } else { // getting - var pos = ele._private.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._private.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 - }, - - renderedBoundingBox: function( options ){ - var bb = this.boundingBox( options ); - var cy = this.cy(); - var zoom = cy.zoom(); - var pan = cy.pan(); - - 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 - }; - }, - - updateCompoundBounds: function(){ - var cy = this.cy(); - - if( !cy.styleEnabled() || !cy.hasCompoundNodes() ){ return cy.collection(); } // save cycles for non compound graphs or when style disabled - - var updated = []; - - function update( parent ){ - var children = parent.children(); - var style = parent._private.style; - var includeLabels = style['compound-sizing-wrt-labels'].value === 'include'; - var bb = children.boundingBox({ includeLabels: includeLabels, includeEdges: true }); - var padding = { - top: style['padding-top'].pfValue, - bottom: style['padding-bottom'].pfValue, - left: style['padding-left'].pfValue, - right: style['padding-right'].pfValue - }; - var pos = parent._private.position; - var didUpdate = false; - - if( style['width'].value === 'auto' ){ - parent._private.autoWidth = bb.w; - pos.x = (bb.x1 + bb.x2 - padding.left + padding.right)/2; - didUpdate = true; - } - - if( style['height'].value === 'auto' ){ - parent._private.autoHeight = bb.h; - pos.y = (bb.y1 + bb.y2 - padding.top + padding.bottom)/2; - didUpdate = true; - } - - if( didUpdate ){ - updated.push( parent ); - } - } - - // go up, level by level - var eles = this.parent(); - while( eles.nonempty() ){ - - // update each parent node in this level - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - - update( ele ); - } - - // next level - eles = eles.parent(); - } - - // return changed - return this.spawn( updated ); - }, - - // get the bounding box of the elements (in raw model position) - boundingBox: function( options ){ - var eles = this; - var cy = eles._private.cy; - var cy_p = cy._private; - var styleEnabled = cy_p.styleEnabled; - - options = options || util.staticEmptyObject(); - - var includeNodes = options.includeNodes === undefined ? true : options.includeNodes; - var includeEdges = options.includeEdges === undefined ? true : options.includeEdges; - var includeLabels = options.includeLabels === undefined ? true : options.includeLabels; - - // recalculate projections etc - if( styleEnabled ){ - cy_p.renderer.recalculateRenderedStyle( this ); - } - - var x1 = Infinity; - var x2 = -Infinity; - var y1 = Infinity; - var y2 = -Infinity; - - // find bounds of elements - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - var _p = ele._private; - var style = _p.style; - var display = styleEnabled ? _p.style['display'].value : 'element'; - var isNode = _p.group === 'nodes'; - var ex1, ex2, ey1, ey2, x, y; - var includedEle = false; - - if( display === 'none' ){ continue; } // then ele doesn't take up space - - if( isNode && includeNodes ){ - includedEle = true; - - var pos = _p.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; - ex2 = x + halfW; - ey1 = y - halfH; - ey2 = y + halfH; - - x1 = ex1 < x1 ? ex1 : x1; - x2 = ex2 > x2 ? ex2 : x2; - y1 = ey1 < y1 ? ey1 : y1; - y2 = ey2 > y2 ? ey2 : y2; - - } else if( ele.isEdge() && includeEdges ){ - includedEle = true; - - var n1 = _p.source; - var n1_p = n1._private; - var n1pos = n1_p.position; - - var n2 = _p.target; - var n2_p = n2._private; - var n2pos = n2_p.position; - - - // handle edge dimensions (rough box estimate) - ////////////////////////////////////////////// - - var rstyle = _p.rstyle || {}; - var w = 0; - var wHalf = 0; - - if( styleEnabled ){ - w = style['width'].pfValue; - wHalf = w/2; - } - - ex1 = n1pos.x; - ex2 = n2pos.x; - ey1 = n1pos.y; - ey2 = n2pos.y; - - if( ex1 > ex2 ){ - var temp = ex1; - ex1 = ex2; - ex2 = temp; - } - - if( ey1 > ey2 ){ - var temp = ey1; - ey1 = ey2; - ey2 = temp; - } - - // take into account edge width - ex1 -= wHalf; - ex2 += wHalf; - ey1 -= wHalf; - ey2 += wHalf; - - x1 = ex1 < x1 ? ex1 : x1; - x2 = ex2 > x2 ? ex2 : x2; - y1 = ey1 < y1 ? ey1 : y1; - y2 = ey2 > y2 ? ey2 : y2; - - // handle points along edge (sanity check) - ////////////////////////////////////////// - - if( styleEnabled ){ - var pts = rstyle.bezierPts || rstyle.linePts || []; - - for( var j = 0; j < pts.length; j++ ){ - var pt = pts[j]; - - ex1 = pt.x - wHalf; - ex2 = pt.x + wHalf; - ey1 = pt.y - wHalf; - ey2 = pt.y + wHalf; - - x1 = ex1 < x1 ? ex1 : x1; - x2 = ex2 > x2 ? ex2 : x2; - y1 = ey1 < y1 ? ey1 : y1; - y2 = ey2 > y2 ? ey2 : y2; - } - } - - // precise haystacks (sanity check) - /////////////////////////////////// - - if( styleEnabled && style['curve-style'].strValue === 'haystack' ){ - var hpts = rstyle.haystackPts; - - ex1 = hpts[0].x; - ey1 = hpts[0].y; - ex2 = hpts[1].x; - ey2 = hpts[1].y; - - if( ex1 > ex2 ){ - var temp = ex1; - ex1 = ex2; - ex2 = temp; - } - - if( ey1 > ey2 ){ - var temp = ey1; - ey1 = ey2; - ey2 = temp; - } - - x1 = ex1 < x1 ? ex1 : x1; - x2 = ex2 > x2 ? ex2 : x2; - y1 = ey1 < y1 ? ey1 : y1; - y2 = ey2 > y2 ? ey2 : y2; - } - - } // edges - - - // handle label dimensions - ////////////////////////// - - if( styleEnabled ){ - - var _p = ele._private; - var style = _p.style; - var rstyle = _p.rstyle; - var label = style['label'].strValue; - var fontSize = style['font-size']; - var halign = style['text-halign']; - var valign = style['text-valign']; - var labelWidth = rstyle.labelWidth; - var labelHeight = rstyle.labelHeight; - var labelX = rstyle.labelX; - var labelY = rstyle.labelY; - var isEdge = ele.isEdge(); - var autorotate = style['edge-text-rotation'].strValue === 'autorotate'; - - if( includeLabels && label && fontSize && labelHeight != null && labelWidth != null && labelX != null && labelY != null && halign && valign ){ - var lh = labelHeight; - var lw = labelWidth; - var lx1, lx2, ly1, ly2; - - if( isEdge ){ - lx1 = labelX - lw/2; - lx2 = labelX + lw/2; - ly1 = labelY - lh/2; - ly2 = labelY + lh/2; - - if( autorotate ){ - var theta = _p.rscratch.labelAngle; - var cos = Math.cos( theta ); - var sin = Math.sin( theta ); - - var rotate = function( x, y ){ - x = x - labelX; - y = y - labelY; - - return { - x: x*cos - y*sin + labelX, - y: x*sin + y*cos + labelY - }; - }; - - var px1y1 = rotate( lx1, ly1 ); - var px1y2 = rotate( lx1, ly2 ); - var px2y1 = rotate( lx2, ly1 ); - var px2y2 = rotate( lx2, ly2 ); - - 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 ); - } - } else { - switch( halign.value ){ - case 'left': - lx1 = labelX - lw; - lx2 = labelX; - break; - - case 'center': - lx1 = labelX - lw/2; - lx2 = labelX + lw/2; - break; - - case 'right': - lx1 = labelX; - lx2 = labelX + lw; - break; - } - - switch( valign.value ){ - case 'top': - ly1 = labelY - lh; - ly2 = labelY; - break; - - case 'center': - ly1 = labelY - lh/2; - ly2 = labelY + lh/2; - break; - - case 'bottom': - ly1 = labelY; - ly2 = labelY + lh; - break; - } - } - - x1 = lx1 < x1 ? lx1 : x1; - x2 = lx2 > x2 ? lx2 : x2; - y1 = ly1 < y1 ? ly1 : y1; - y2 = ly2 > y2 ? ly2 : y2; - } - } // style enabled for labels - } // for - - var noninf = function(x){ - if( x === Infinity || x === -Infinity ){ - return 0; - } - - return x; - }; - - x1 = noninf(x1); - x2 = noninf(x2); - y1 = noninf(y1); - y2 = noninf(y2); - - return { - x1: x1, - x2: x2, - y1: y1, - y2: y2, - w: x2 - x1, - h: y2 - y1 - }; - } -}); - -var defineDimFns = function( 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 _p = ele._private; - var cy = _p.cy; - var styleEnabled = cy._private.styleEnabled; - - if( ele ){ - if( styleEnabled ){ - var d = _p.style[ opts.name ]; - - switch( d.strValue ){ - case 'auto': - return _p[ opts.autoName ] || 0; - case 'label': - return _p.rstyle[ opts.labelName ] || 0; - default: - return d.pfValue; - } - } else { - return 1; - } - } - }; - - 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 style = _p.style; - var dim = ele[ opts.name ](); - var border = style['border-width'].pfValue; - var padding = style[ opts.paddings[0] ].pfValue + style[ opts.paddings[1] ].pfValue; - - return dim + border + padding; - } else { - return 1; - } - } - }; - - fn[ 'rendered' + opts.uppercaseName ] = function renderedDimImpl(){ - var ele = this[0]; - - if( ele ){ - var d = ele[ opts.name ](); - return d * this.cy().zoom(); - } - }; - - fn[ 'rendered' + opts.uppercaseOuterName ] = function renderedOuterDimImpl(){ - var ele = this[0]; - - if( ele ){ - var od = ele[ opts.outerName ](); - return od * this.cy().zoom(); - } - }; -}; - -defineDimFns({ - name: 'width', - paddings: ['padding-left', 'padding-right'] -}); - -defineDimFns({ - name: 'height', - paddings: ['padding-top', 'padding-bottom'] -}); - -// aliases -fn.modelPosition = fn.point = fn.position; -fn.modelPositions = fn.points = fn.positions; -fn.renderedPoint = fn.renderedPosition; -fn.relativePoint = fn.relativePosition; -fn.boundingbox = fn.boundingBox; -fn.renderedBoundingbox = fn.renderedBoundingBox; - -module.exports = elesfn; - -},{"../define":41,"../is":77,"../util":94}],19:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); -var is = _dereq_('../is'); - -// represents a node or an edge -var Element = function(cy, params, restore){ - if( !(this instanceof Element) ){ - return new Element(cy, params, restore); - } - - var self = this; - restore = (restore === undefined || restore ? true : false); - - if( cy === undefined || params === undefined || !is.core(cy) ){ - util.error('An element must have a core reference and parameters set'); - return; - } - - var group = params.group; - - // try to automatically infer the group if unspecified - if( group == null ){ - if( params.data.source != null && params.data.target != null ){ - group = 'edges'; - } else { - group = 'nodes'; - } - } - - // validate group - if( group !== 'nodes' && group !== 'edges' ){ - util.error('An element must be of type `nodes` or `edges`; you specified `' + group + '`'); - return; - } - - // make the element array-like, just like a collection - this.length = 1; - this[0] = this; - - // NOTE: when something is added here, add also to ele.json() - this._private = { - cy: cy, - single: true, // indicates this is an element - data: params.data || {}, // data object - position: params.position || {}, // (x, y) position pair - autoWidth: undefined, // width and height of nodes calculated by the renderer when set to special 'auto' value - autoHeight: undefined, - listeners: [], // array of bound listeners - group: group, // string; 'nodes' or 'edges' - style: {}, // properties as set by the style - rstyle: {}, // properties for style sent from the renderer to the core - styleCxts: [], // applied style contexts from the styler - removed: true, // whether it's inside the vis; true if removed (set true here since we call restore) - selected: params.selected ? true : false, // whether it's selected - selectable: params.selectable === undefined ? true : ( params.selectable ? true : false ), // whether it's selectable - locked: params.locked ? true : false, // whether the element is locked (cannot be moved) - grabbed: false, // whether the element is grabbed by the mouse; renderer sets this privately - grabbable: params.grabbable === undefined ? true : ( params.grabbable ? true : false ), // whether the element can be grabbed - active: false, // whether the element is active from user interaction - classes: {}, // map ( className => true ) - animation: { // object for currently-running animations - current: [], - queue: [] - }, - rscratch: {}, // object in which the renderer can store information - scratch: params.scratch || {}, // scratch objects - edges: [], // array of connected edges - children: [] // array of children - }; - - // renderedPosition overrides if specified - if( params.renderedPosition ){ - var rpos = params.renderedPosition; - var pan = cy.pan(); - var zoom = cy.zoom(); - - this._private.position = { - x: (rpos.x - pan.x)/zoom, - y: (rpos.y - pan.y)/zoom - }; - } - - if( is.string(params.classes) ){ - var classes = params.classes.split(/\s+/); - for( var i = 0, l = classes.length; i < l; i++ ){ - var cls = classes[i]; - if( !cls || cls === '' ){ continue; } - - self._private.classes[cls] = true; - } - } - - if( params.style || params.css ){ - cy.style().applyBypass( this, params.style || params.css ); - } - - if( restore === undefined || restore ){ - this.restore(); - } - -}; - -module.exports = Element; - -},{"../is":77,"../util":94}],20:[function(_dereq_,module,exports){ -'use strict'; - -var define = _dereq_('../define'); - -var elesfn = ({ - on: define.on(), // .on( events [, selector] [, data], handler) - one: define.on({ unbindSelfOnTrigger: true }), - once: define.on({ unbindAllBindersOnTrigger: true }), - off: define.off(), // .off( events [, selector] [, handler] ) - trigger: define.trigger(), // .trigger( events [, extraParams] ) - - rtrigger: function(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, - collection: this - }); - - this.trigger(event, extraParams); - return this; - } -}); - -// aliases: -define.eventAliasesOn( elesfn ); - -module.exports = elesfn; - -},{"../define":41}],21:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var Selector = _dereq_('../selector'); - -var elesfn = ({ - nodes: function( selector ){ - return this.filter(function(i, element){ - return element.isNode(); - }).filter(selector); - }, - - edges: function( selector ){ - return this.filter(function(i, element){ - return element.isEdge(); - }).filter(selector); - }, - - filter: function( filter ){ - if( is.fn(filter) ){ - var elements = []; - - for( var i = 0; i < this.length; i++ ){ - var ele = this[i]; - - if( filter.apply(ele, [i, ele]) ){ - elements.push(ele); - } - } - - return this.spawn(elements); - - } else if( is.string(filter) || is.elementOrCollection(filter) ){ - return Selector(filter).filter(this); - - } else if( filter === undefined ){ - return this; - } - - return this.spawn(); // if not handled by above, give 'em an empty collection - }, - - not: function( toRemove ){ - if( !toRemove ){ - return this; - } else { - - if( is.string( toRemove ) ){ - toRemove = this.filter( toRemove ); - } - - var elements = []; - - for( var i = 0; i < this.length; i++ ){ - var element = this[i]; - - var remove = toRemove._private.ids[ element.id() ]; - if( !remove ){ - elements.push( element ); - } - } - - return this.spawn( elements ); - } - - }, - - absoluteComplement: function(){ - var cy = this._private.cy; - - return cy.elements().not( this ); - }, - - intersect: function( other ){ - // if a selector is specified, then filter by it instead - if( is.string(other) ){ - var selector = other; - return this.filter( selector ); - } - - var elements = []; - var col1 = this; - var col2 = other; - var col1Smaller = this.length < other.length; - // var ids1 = col1Smaller ? col1._private.ids : col2._private.ids; - var ids2 = col1Smaller ? col2._private.ids : col1._private.ids; - var col = col1Smaller ? col1 : col2; - - for( var i = 0; i < col.length; i++ ){ - var id = col[i]._private.data.id; - var ele = ids2[ id ]; - - if( ele ){ - elements.push( ele ); - } - } - - return this.spawn( elements ); - }, - - xor: function( other ){ - var cy = this._private.cy; - - if( is.string(other) ){ - other = cy.$( other ); - } - - var elements = []; - var col1 = this; - var col2 = other; - - var add = function( col, other ){ - - for( var i = 0; i < col.length; i++ ){ - var ele = col[i]; - var id = ele._private.data.id; - var inOther = other._private.ids[ id ]; - - if( !inOther ){ - elements.push( ele ); - } - } - - }; - - add( col1, col2 ); - add( col2, col1 ); - - return this.spawn( elements ); - }, - - diff: function( other ){ - var cy = this._private.cy; - - if( is.string(other) ){ - other = cy.$( other ); - } - - var left = []; - var right = []; - var both = []; - var col1 = this; - var col2 = other; - - var add = function( col, other, retEles ){ - - for( var i = 0; i < col.length; i++ ){ - var ele = col[i]; - var id = ele._private.data.id; - var inOther = other._private.ids[ id ]; - - if( inOther ){ - both.push( ele ); - } else { - retEles.push( ele ); - } - } - - }; - - add( col1, col2, left ); - add( col2, col1, right ); - - return { - left: this.spawn( left, { unique: true } ), - right: this.spawn( right, { unique: true } ), - both: this.spawn( both, { unique: true } ) - }; - }, - - add: function( toAdd ){ - var cy = this._private.cy; - - if( !toAdd ){ - return this; - } - - if( is.string(toAdd) ){ - var selector = toAdd; - toAdd = cy.elements(selector); - } - - var elements = []; - - for( var i = 0; i < this.length; i++ ){ - elements.push( this[i] ); - } - - for( var i = 0; i < toAdd.length; i++ ){ - - var add = !this._private.ids[ toAdd[i].id() ]; - if( add ){ - elements.push( toAdd[i] ); - } - } - - return this.spawn(elements); - }, - - // in place merge on calling collection - merge: function( toAdd ){ - var _p = this._private; - var cy = _p.cy; - - if( !toAdd ){ - return this; - } - - if( is.string(toAdd) ){ - var selector = toAdd; - toAdd = cy.elements(selector); - } - - for( var i = 0; i < toAdd.length; i++ ){ - var toAddEle = toAdd[i]; - var id = toAddEle.id(); - var add = !_p.ids[ id ]; - - if( add ){ - var index = this.length++; - - this[ index ] = toAddEle; - _p.ids[ id ] = toAddEle; - _p.indexes[ id ] = index; - } - } - - return this; // chaining - }, - - // remove single ele in place in calling collection - unmergeOne: function( ele ){ - ele = ele[0]; - - var _p = this._private; - var id = ele.id(); - var i = _p.indexes[ id ]; - - if( i == null ){ - return this; // no need to remove - } - - // remove ele - this[i] = undefined; - _p.ids[ id ] = undefined; - _p.indexes[ id ] = undefined; - - var unmergedLastEle = i === this.length - 1; - - // replace empty spot with last ele in collection - if( this.length > 1 && !unmergedLastEle ){ - var lastEleI = this.length - 1; - var lastEle = this[ lastEleI ]; - - this[ lastEleI ] = undefined; - this[i] = lastEle; - _p.indexes[ lastEle.id() ] = i; - } - - // the collection is now 1 ele smaller - this.length--; - - return this; - }, - - // remove eles in place on calling collection - unmerge: function( toRemove ){ - var cy = this._private.cy; - - if( !toRemove ){ - return this; - } - - if( is.string(toRemove) ){ - var selector = toRemove; - toRemove = cy.elements(selector); - } - - for( var i = 0; i < toRemove.length; i++ ){ - this.unmergeOne( toRemove[i] ); - } - - return this; // chaining - }, - - map: function( mapFn, thisArg ){ - var arr = []; - var eles = this; - - 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 ); - - arr.push( ret ); - } - - return arr; - }, - - stdFilter: function( fn, thisArg ){ - var filterEles = []; - var eles = this; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - var include = thisArg ? fn.apply( thisArg, [ele, i, eles] ) : fn( ele, i, eles ); - - if( include ){ - filterEles.push( ele ); - } - } - - return this.spawn( filterEles ); - }, - - max: function( valFn, thisArg ){ - var max = -Infinity; - var maxEle; - 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 { - value: max, - ele: maxEle - }; - }, - - min: function( valFn, thisArg ){ - var min = Infinity; - var minEle; - 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 < min ){ - min = val; - minEle = ele; - } - } - - return { - value: min, - ele: minEle - }; - } -}); - -// 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.complement = fn.abscomp = fn.absoluteComplement; - -module.exports = elesfn; - -},{"../is":77,"../selector":81}],22:[function(_dereq_,module,exports){ -'use strict'; - -var elesfn = ({ - isNode: function(){ - return this.group() === 'nodes'; - }, - - isEdge: function(){ - return this.group() === 'edges'; - }, - - isLoop: function(){ - return this.isEdge() && this.source().id() === this.target().id(); - }, - - isSimple: function(){ - return this.isEdge() && this.source().id() !== this.target().id(); - }, - - group: function(){ - var ele = this[0]; - - if( ele ){ - return ele._private.group; - } - } -}); - - -module.exports = elesfn; - -},{}],23:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); -var is = _dereq_('../is'); - -var Element = _dereq_('./element'); - -// factory for generating edge ids when no id is specified for a new element -var idFactory = { - prefix: 'ele', - id: 0, - generate: function(cy, element, tryThisId){ - var json = is.element( element ) ? element._private : element; - var id = tryThisId != null ? tryThisId : this.prefix + this.id; - - if( cy.getElementById(id).empty() ){ - this.id++; // we've used the current id, so move it up - } else { // otherwise keep trying successive unused ids - while( !cy.getElementById(id).empty() ){ - id = this.prefix + ( ++this.id ); - } - } - - return id; - } -}; - -// represents a set of nodes, edges, or both together -var Collection = function(cy, elements, options){ - if( !(this instanceof Collection) ){ - return new Collection(cy, elements, options); - } - - if( cy === undefined || !is.core(cy) ){ - util.error('A collection must have a reference to the core'); - return; - } - - var ids = {}; - var indexes = {}; - var createdElements = false; - - if( !elements ){ - elements = []; - } else if( elements.length > 0 && is.plainObject( elements[0] ) && !is.element( elements[0] ) ){ - createdElements = true; - - // make elements from json and restore all at once later - var eles = []; - var elesIds = {}; - - for( var i = 0, l = elements.length; i < l; i++ ){ - var json = elements[i]; - - if( json.data == null ){ - json.data = {}; - } - - var data = json.data; - - // make sure newly created elements have valid ids - if( data.id == null ){ - data.id = idFactory.generate( cy, json ); - } else if( cy.getElementById( data.id ).length !== 0 || elesIds[ data.id ] ){ - continue; // can't create element if prior id already exists - } - - var ele = new Element( cy, json, false ); - eles.push( ele ); - elesIds[ data.id ] = true; - } - - elements = eles; - } - - this.length = 0; - - for( var i = 0, l = elements.length; i < l; i++ ){ - var element = elements[i]; - if( !element ){ continue; } - - var id = element._private.data.id; - - if( !options || (options.unique && !ids[ id ] ) ){ - ids[ id ] = element; - indexes[ id ] = this.length; - - this[ this.length ] = element; - this.length++; - } - } - - this._private = { - cy: cy, - ids: ids, - indexes: indexes - }; - - // restore the elements if we created them from json - if( createdElements ){ - this.restore(); - } -}; - -// Functions -//////////////////////////////////////////////////////////////////////////////////////////////////// - -// keep the prototypes in sync (an element has the same functions as a collection) -// and use elefn and elesfn as shorthands to the prototypes -var elesfn = Element.prototype = Collection.prototype; - -elesfn.instanceString = function(){ - return 'collection'; -}; - -elesfn.spawn = function( cy, eles, opts ){ - if( !is.core(cy) ){ // cy is optional - opts = eles; - eles = cy; - cy = this.cy(); - } - - return new Collection( cy, eles, opts ); -}; - -elesfn.cy = function(){ - return this._private.cy; -}; - -elesfn.element = function(){ - return this[0]; -}; - -elesfn.collection = function(){ - if( is.collection(this) ){ - return this; - } else { // an element - return new Collection( this._private.cy, [this] ); - } -}; - -elesfn.unique = function(){ - return new Collection( this._private.cy, this, { unique: true } ); -}; - -elesfn.getElementById = function( id ){ - var cy = this._private.cy; - var ele = this._private.ids[ id ]; - - return ele ? ele : new Collection(cy); // get ele or empty collection -}; - -elesfn.json = function( obj ){ - var ele = this.element(); - var cy = this.cy(); - - if( ele == null && obj ){ return this; } // can't set to no eles - - if( ele == null ){ return undefined; } // can't get from no eles - - var p = ele._private; - - if( is.plainObject(obj) ){ // set - - cy.startBatch(); - - if( obj.data ){ - ele.data( obj.data ); - } - - if( obj.position ){ - ele.position( obj.position ); - } - - // ignore group -- immutable - - var checkSwitch = function( k, trueFnName, falseFnName ){ - var obj_k = obj[k]; - - if( obj_k != null && obj_k !== p[k] ){ - if( obj_k ){ - ele[ trueFnName ](); - } else { - ele[ falseFnName ](); - } - } - }; - - checkSwitch( 'removed', 'remove', 'restore' ); - - checkSwitch( 'selected', 'select', 'unselect' ); - - checkSwitch( 'selectable', 'selectify', 'unselectify' ); - - checkSwitch( 'locked', 'lock', 'unlock' ); - - checkSwitch( 'grabbable', 'grabify', 'ungrabify' ); - - if( obj.classes != null ){ - ele.classes( obj.classes ); - } - - cy.endBatch(); - - return this; - - } else if( obj === undefined ){ // get - - var json = { - data: util.copy( p.data ), - position: util.copy( p.position ), - group: p.group, - removed: p.removed, - selected: p.selected, - selectable: p.selectable, - locked: p.locked, - grabbable: p.grabbable, - classes: null - }; - - var classes = []; - for( var cls in p.classes ){ - if( p.classes[cls] ){ - classes.push(cls); - } - } - json.classes = classes.join(' '); - - return json; - } -}; - -elesfn.jsons = function(){ - var jsons = []; - - for( var i = 0; i < this.length; i++ ){ - var ele = this[i]; - var json = ele.json(); - - jsons.push( json ); - } - - return jsons; -}; - -elesfn.clone = function(){ - var cy = this.cy(); - var elesArr = []; - - for( var i = 0; i < this.length; i++ ){ - var ele = this[i]; - var json = ele.json(); - var clone = new Element(cy, json, false); // NB no restore - - elesArr.push( clone ); - } - - return new Collection( cy, elesArr ); -}; -elesfn.copy = elesfn.clone; - -elesfn.restore = function( notifyRenderer ){ - var self = this; - var restored = []; - var cy = self.cy(); - - if( notifyRenderer === undefined ){ - notifyRenderer = true; - } - - // create arrays of nodes and edges, since we need to - // restore the nodes first - var elements = []; - var nodes = [], edges = []; - var numNodes = 0; - var numEdges = 0; - for( var i = 0, l = self.length; i < l; i++ ){ - var ele = self[i]; - - // keep nodes first in the array and edges after - if( ele.isNode() ){ // put to front of array if node - nodes.push( ele ); - numNodes++; - } else { // put to end of array if edge - edges.push( ele ); - numEdges++; - } - } - - elements = nodes.concat( edges ); - - // now, restore each element - for( var i = 0, l = elements.length; i < l; i++ ){ - var ele = elements[i]; - - if( !ele.removed() ){ - // don't need to do anything - continue; - } - - var _private = ele._private; - var data = _private.data; - - // set id and validate - if( data.id === undefined ){ - data.id = idFactory.generate( cy, ele ); - - } else if( is.number(data.id) ){ - data.id = '' + data.id; // now it's a string - - } else if( is.emptyString(data.id) || !is.string(data.id) ){ - util.error('Can not create element with invalid string ID `' + data.id + '`'); - - // can't create element if it has empty string as id or non-string id - continue; - } else if( cy.getElementById( data.id ).length !== 0 ){ - util.error('Can not create second element with ID `' + data.id + '`'); - - // can't create element if one already has that id - continue; - } - - var id = data.id; // id is finalised, now let's keep a ref - - if( ele.isNode() ){ // extra checks for nodes - var node = ele; - var pos = _private.position; - - // make sure the nodes have a defined position - - if( pos.x == null ){ - pos.x = 0; - } - - if( pos.y == null ){ - pos.y = 0; - } - } - - if( ele.isEdge() ){ // extra checks for edges - - var edge = ele; - var fields = ['source', 'target']; - var fieldsLength = fields.length; - var badSourceOrTarget = false; - for(var j = 0; j < fieldsLength; j++){ - - var field = fields[j]; - var val = data[field]; - - if( is.number(val) ){ - val = data[field] = '' + data[field]; // now string - } - - if( val == null || val === '' ){ - // can't create if source or target is not defined properly - util.error('Can not create edge `' + id + '` with unspecified ' + field); - badSourceOrTarget = true; - } else if( cy.getElementById(val).empty() ){ - // can't create edge if one of its nodes doesn't exist - util.error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`'); - badSourceOrTarget = true; - } - } - - if( badSourceOrTarget ){ continue; } // can't create this - - var src = cy.getElementById( data.source ); - var tgt = cy.getElementById( data.target ); - - src._private.edges.push( edge ); - tgt._private.edges.push( edge ); - - edge._private.source = src; - edge._private.target = tgt; - - } // if is edge - - // create mock ids map for element so it can be used like collections - _private.ids = {}; - _private.ids[ id ] = ele; - - _private.removed = false; - cy.addToPool( ele ); - - restored.push( ele ); - } // for each element - - // do compound node sanity checks - for( var i = 0; i < numNodes; i++ ){ // each node - var node = elements[i]; - var data = node._private.data; - - if( is.number(data.parent) ){ // then automake string - data.parent = '' + data.parent; - } - - var parentId = data.parent; - - var specifiedParent = parentId != null; - - if( specifiedParent ){ - var parent = cy.getElementById( parentId ); - - if( parent.empty() ){ - // non-existant parent; just remove it - data.parent = undefined; - } else { - var selfAsParent = false; - var ancestor = parent; - while( !ancestor.empty() ){ - if( node.same(ancestor) ){ - // mark self as parent and remove from data - selfAsParent = true; - data.parent = undefined; // remove parent reference - - // exit or we loop forever - break; - } - - ancestor = ancestor.parent(); - } - - if( !selfAsParent ){ - // connect with children - parent[0]._private.children.push( node ); - node._private.parent = parent[0]; - - // let the core know we have a compound graph - cy._private.hasCompoundNodes = true; - } - } // else - } // if specified parent - } // for each node - - restored = new Collection( cy, restored ); - if( restored.length > 0 ){ - - var toUpdateStyle = restored.add( restored.connectedNodes() ).add( restored.parent() ); - toUpdateStyle.updateStyle( notifyRenderer ); - - if( notifyRenderer ){ - restored.rtrigger('add'); - } else { - restored.trigger('add'); - } - } - - return self; // chainability -}; - -elesfn.removed = function(){ - var ele = this[0]; - return ele && ele._private.removed; -}; - -elesfn.inside = function(){ - var ele = this[0]; - return ele && !ele._private.removed; -}; - -elesfn.remove = function( notifyRenderer ){ - var self = this; - var removed = []; - var elesToRemove = []; - var elesToRemoveIds = {}; - var cy = self._private.cy; - - if( notifyRenderer === undefined ){ - notifyRenderer = true; - } - - // add connected edges - function addConnectedEdges(node){ - var edges = node._private.edges; - for( var i = 0; i < edges.length; i++ ){ - add( edges[i] ); - } - } - - - // add descendant nodes - function addChildren(node){ - var children = node._private.children; - - for( var i = 0; i < children.length; i++ ){ - add( children[i] ); - } - } - - function add( ele ){ - var alreadyAdded = elesToRemoveIds[ ele.id() ]; - if( alreadyAdded ){ - return; - } else { - elesToRemoveIds[ ele.id() ] = true; - } - - if( ele.isNode() ){ - elesToRemove.push( ele ); // nodes are removed last - - addConnectedEdges( ele ); - addChildren( ele ); - } else { - elesToRemove.unshift( ele ); // edges are removed first - } - } - - // make the list of elements to remove - // (may be removing more than specified due to connected edges etc) - - for( var i = 0, l = self.length; i < l; i++ ){ - var ele = self[i]; - - add( ele ); - } - - function removeEdgeRef(node, edge){ - var connectedEdges = node._private.edges; - for( var j = 0; j < connectedEdges.length; j++ ){ - var connectedEdge = connectedEdges[j]; - - if( edge === connectedEdge ){ - connectedEdges.splice( j, 1 ); - break; - } - } - } - - function removeChildRef(parent, ele){ - ele = ele[0]; - parent = parent[0]; - var children = parent._private.children; - - for( var j = 0; j < children.length; j++ ){ - if( children[j][0] === ele[0] ){ - children.splice(j, 1); - break; - } - } - } - - for( var i = 0; i < elesToRemove.length; i++ ){ - var ele = elesToRemove[i]; - - // mark as removed - ele._private.removed = true; - - // remove from core pool - cy.removeFromPool( ele ); - - // add to list of removed elements - removed.push( ele ); - - if( ele.isEdge() ){ // remove references to this edge in its connected nodes - var src = ele.source()[0]; - var tgt = ele.target()[0]; - - removeEdgeRef( src, ele ); - removeEdgeRef( tgt, ele ); - - } else { // remove reference to parent - var parent = ele.parent(); - - if( parent.length !== 0 ){ - removeChildRef(parent, ele); - } - } - } - - // check to see if we have a compound graph or not - var elesStillInside = cy._private.elements; - cy._private.hasCompoundNodes = false; - for( var i = 0; i < elesStillInside.length; i++ ){ - var ele = elesStillInside[i]; - - if( ele.isParent() ){ - cy._private.hasCompoundNodes = true; - break; - } - } - - var removedElements = new Collection( this.cy(), removed ); - if( removedElements.size() > 0 ){ - // must manually notify since trigger won't do this automatically once removed - - if( notifyRenderer ){ - this.cy().notify({ - type: 'remove', - collection: removedElements - }); - } - - removedElements.trigger('remove'); - } - - // check for empty remaining parent nodes - var checkedParentId = {}; - for( var i = 0; i < elesToRemove.length; i++ ){ - var ele = elesToRemove[i]; - var isNode = ele._private.group === 'nodes'; - var parentId = ele._private.data.parent; - - if( isNode && parentId !== undefined && !checkedParentId[ parentId ] ){ - checkedParentId[ parentId ] = true; - var parent = cy.getElementById( parentId ); - - if( parent && parent.length !== 0 && !parent._private.removed && parent.children().length === 0 ){ - parent.updateStyle(); - } - } - } - - return new Collection( cy, removed ); -}; - -elesfn.move = function( struct ){ - var cy = this._private.cy; - - if( struct.source !== undefined || struct.target !== undefined ){ - var srcId = struct.source; - var tgtId = struct.target; - var srcExists = cy.getElementById( srcId ).length > 0; - var tgtExists = cy.getElementById( tgtId ).length > 0; - - if( srcExists || tgtExists ){ - var jsons = this.jsons(); - - this.remove(); - - for( var i = 0; i < jsons.length; i++ ){ - var json = jsons[i]; - - if( json.group === 'edges' ){ - if( srcExists ){ json.data.source = srcId; } - if( tgtExists ){ json.data.target = tgtId; } - } - } - - return cy.add( jsons ); - } - - } else if( struct.parent !== undefined ){ // move node to new parent - var parentId = struct.parent; - var parentExists = parentId === null || cy.getElementById( parentId ).length > 0; - - if( parentExists ){ - var jsons = this.jsons(); - var descs = this.descendants(); - var descsEtc = descs.merge( descs.add(this).connectedEdges() ); - - this.remove(); // NB: also removes descendants and their connected edges - - for( var i = 0; i < this.length; i++ ){ - var json = jsons[i]; - - if( json.group === 'nodes' ){ - json.data.parent = parentId === null ? undefined : parentId; - } - } - } - - return cy.add( jsons ).merge( descsEtc.restore() ); - } - - return this; // if nothing done -}; - -[ - _dereq_('./algorithms'), - _dereq_('./animation'), - _dereq_('./class'), - _dereq_('./comparators'), - _dereq_('./compounds'), - _dereq_('./data'), - _dereq_('./degree'), - _dereq_('./dimensions'), - _dereq_('./events'), - _dereq_('./filter'), - _dereq_('./group'), - _dereq_('./index'), - _dereq_('./iteration'), - _dereq_('./layout'), - _dereq_('./style'), - _dereq_('./switch-functions'), - _dereq_('./traversing') -].forEach(function( props ){ - util.extend( elesfn, props ); -}); - -module.exports = Collection; - -},{"../is":77,"../util":94,"./algorithms":9,"./animation":12,"./class":13,"./comparators":14,"./compounds":15,"./data":16,"./degree":17,"./dimensions":18,"./element":19,"./events":20,"./filter":21,"./group":22,"./index":23,"./iteration":24,"./layout":25,"./style":26,"./switch-functions":27,"./traversing":28}],24:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var zIndexSort = _dereq_('./zsort'); - -var elesfn = ({ - each: function(fn){ - if( is.fn(fn) ){ - for(var i = 0; i < this.length; i++){ - var ele = this[i]; - var ret = fn.apply( ele, [ i, ele ] ); - - if( ret === false ){ break; } // exit each early on return false - } - } - return this; - }, - - forEach: function(fn, thisArg){ - if( is.fn(fn) ){ - - 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 ); - - if( ret === false ){ break; } // exit each early on return false - } - } - - return this; - }, - - toArray: function(){ - var array = []; - - for(var i = 0; i < this.length; i++){ - array.push( this[i] ); - } - - return array; - }, - - slice: function(start, end){ - var array = []; - var thisSize = this.length; - - if( end == null ){ - end = thisSize; - } - - if( start == null ){ - start = 0; - } - - if( start < 0 ){ - start = thisSize + start; - } - - if( end < 0 ){ - end = thisSize + end; - } - - for(var i = start; i >= 0 && i < end && i < thisSize; i++){ - array.push( this[i] ); - } - - return this.spawn(array); - }, - - size: function(){ - return this.length; - }, - - eq: function(i){ - return this[i] || this.spawn(); - }, - - first: function(){ - return this[0] || this.spawn(); - }, - - last: function(){ - return this[ this.length - 1 ] || this.spawn(); - }, - - empty: function(){ - return this.length === 0; - }, - - nonempty: function(){ - return !this.empty(); - }, - - sort: function( sortFn ){ - if( !is.fn( sortFn ) ){ - return this; - } - - var sorted = this.toArray().sort( sortFn ); - - return this.spawn(sorted); - }, - - sortByZIndex: function(){ - return this.sort( zIndexSort ); - }, - - zDepth: function(){ - var ele = this[0]; - if( !ele ){ return undefined; } - - // var cy = ele.cy(); - var _p = ele._private; - var group = _p.group; - - if( group === 'nodes' ){ - var depth = _p.data.parent ? ele.parents().size() : 0; - - if( !ele.isParent() ){ - return Number.MAX_VALUE; // childless nodes always on top - } - - 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 = elesfn; - -},{"../is":77,"./zsort":29}],25:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var util = _dereq_('../util'); - -var elesfn = ({ - - // using standard layout options, apply position function (w/ or w/o animation) - layoutPositions: function( layout, options, fn ){ - var nodes = this.nodes(); - var cy = this.cy(); - - layout.trigger({ type: 'layoutstart', layout: layout }); - - layout.animations = []; - - if( options.animate ){ - for( var i = 0; i < nodes.length; i++ ){ - var node = nodes[i]; - var lastNode = i === nodes.length - 1; - - var newPos = fn.call( node, i, node ); - var pos = node.position(); - - if( !is.number(pos.x) || !is.number(pos.y) ){ - node.silentPosition({ x: 0, y: 0 }); - } - - var ani = node.animation({ - position: newPos, - duration: options.animationDuration, - easing: options.animationEasing, - step: !lastNode ? undefined : function(){ - if( options.fit ){ - cy.fit( options.eles, options.padding ); - } - }, - complete: !lastNode ? undefined : function(){ - if( options.zoom != null ){ - cy.zoom( options.zoom ); - } - - if( options.pan ){ - cy.pan( options.pan ); - } - - if( options.fit ){ - cy.fit( options.eles, options.padding ); - } - - layout.one('layoutstop', options.stop); - layout.trigger({ type: 'layoutstop', layout: layout }); - } - }); - - layout.animations.push( ani ); - - ani.play(); - } - - layout.one('layoutready', options.ready); - layout.trigger({ type: 'layoutready', layout: layout }); - } else { - nodes.positions( fn ); - - if( options.fit ){ - cy.fit( options.eles, options.padding ); - } - - if( options.zoom != null ){ - cy.zoom( options.zoom ); - } - - if( options.pan ){ - cy.pan( options.pan ); - } - - layout.one('layoutready', options.ready); - layout.trigger({ type: 'layoutready', layout: layout }); - - layout.one('layoutstop', options.stop); - layout.trigger({ type: 'layoutstop', layout: layout }); - } - - return this; // chaining - }, - - layout: function( options ){ - var cy = this.cy(); - - cy.layout( util.extend({}, options, { - eles: this - }) ); - - return this; - }, - - makeLayout: function( options ){ - var cy = this.cy(); - - return cy.makeLayout( util.extend({}, options, { - eles: this - }) ); - } - -}); - -// aliases: -elesfn.createLayout = elesfn.makeLayout; - -module.exports = elesfn; - -},{"../is":77,"../util":94}],26:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); - -var elesfn = ({ - - // fully updates (recalculates) the style for the elements - updateStyle: function( notifyRenderer ){ - var cy = this._private.cy; - - if( !cy.styleEnabled() ){ return this; } - - if( cy._private.batchingStyle ){ - var bEles = cy._private.batchStyleEles; - - bEles.merge( this ); - - return this; // chaining and exit early when batching - } - - var style = cy.style(); - notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false; - - style.apply( this ); - - var updatedCompounds = this.updateCompoundBounds(); - var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this; - - if( notifyRenderer ){ - toNotify.rtrigger('style'); // let renderer know we changed style - } else { - toNotify.trigger('style'); // just fire the event - } - return this; // chaining - }, - - // just update the mappers in the elements' styles; cheaper than eles.updateStyle() - updateMappers: function( notifyRenderer ){ - var cy = this._private.cy; - var style = cy.style(); - notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false; - - if( !cy.styleEnabled() ){ return this; } - - style.updateMappers( this ); - - var updatedCompounds = this.updateCompoundBounds(); - var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this; - - if( notifyRenderer ){ - toNotify.rtrigger('style'); // let renderer know we changed style - } else { - toNotify.trigger('style'); // just fire the event - } - return this; // chaining - }, - - // 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) - renderedCss: function( property ){ - var cy = this.cy(); - if( !cy.styleEnabled() ){ return this; } - - var ele = this[0]; - - if( ele ){ - var renstyle = ele.cy().style().getRenderedStyle( ele ); - - if( property === undefined ){ - return renstyle; - } else { - return renstyle[ property ]; - } - } - }, - - // read the calculated css style of the element or override the style (via a bypass) - css: function( name, value ){ - var cy = this.cy(); - - if( !cy.styleEnabled() ){ return this; } - - var updateTransitions = false; - var style = cy.style(); - - if( is.plainObject(name) ){ // then extend the bypass - var props = name; - style.applyBypass( this, props, updateTransitions ); - - var updatedCompounds = this.updateCompoundBounds(); - var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this; - toNotify.rtrigger('style'); // let the renderer know we've updated style - - } else if( is.string(name) ){ - - if( value === undefined ){ // then get the property from the style - var ele = this[0]; - - 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 updatedCompounds = this.updateCompoundBounds(); - var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this; - toNotify.rtrigger('style'); // let the renderer know we've updated style - } - - } else if( name === undefined ){ - var ele = this[0]; - - if( ele ){ - return style.getRawStyle( ele ); - } else { // empty collection => can't get any value - return; - } - } - - return this; // chaining - }, - - removeCss: function( names ){ - var cy = this.cy(); - - if( !cy.styleEnabled() ){ return this; } - - var updateTransitions = false; - var style = cy.style(); - var eles = this; - - if( names === undefined ){ - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - - style.removeAllBypasses( ele, updateTransitions ); - } - } else { - names = names.split(/\s+/); - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - - style.removeBypasses( ele, names, updateTransitions ); - } - } - - var updatedCompounds = this.updateCompoundBounds(); - var toNotify = updatedCompounds.length > 0 ? this.add( updatedCompounds ) : this; - toNotify.rtrigger('style'); // let the renderer know we've updated style - - return this; // chaining - }, - - show: function(){ - this.css('display', 'element'); - return this; // chaining - }, - - hide: function(){ - this.css('display', 'none'); - return this; // chaining - }, - - visible: function(){ - var cy = this.cy(); - if( !cy.styleEnabled() ){ return true; } - - var ele = this[0]; - var hasCompoundNodes = cy.hasCompoundNodes(); - - if( ele ){ - var style = ele._private.style; - - if( - style['visibility'].value !== 'visible' - || style['display'].value !== 'element' - ){ - return false; - } - - if( ele._private.group === 'nodes' ){ - if( !hasCompoundNodes ){ return true; } - - var parents = ele._private.data.parent ? ele.parents() : null; - - if( parents ){ - for( var i = 0; i < parents.length; i++ ){ - var parent = parents[i]; - var pStyle = parent._private.style; - var pVis = pStyle['visibility'].value; - var pDis = pStyle['display'].value; - - if( pVis !== 'visible' || pDis !== 'element' ){ - return false; - } - } - } - - return true; - } else { - var src = ele._private.source; - var tgt = ele._private.target; - - return src.visible() && tgt.visible(); - } - - } - }, - - hidden: function(){ - var ele = this[0]; - - if( ele ){ - return !ele.visible(); - } - }, - - effectiveOpacity: function(){ - var cy = this.cy(); - if( !cy.styleEnabled() ){ return 1; } - - var hasCompoundNodes = cy.hasCompoundNodes(); - var ele = this[0]; - - if( ele ){ - var _p = ele._private; - var parentOpacity = _p.style.opacity.value; - - if( !hasCompoundNodes ){ return parentOpacity; } - - var parents = !_p.data.parent ? null : ele.parents(); - - if( parents ){ - for( var i = 0; i < parents.length; i++ ){ - var parent = parents[i]; - var opacity = parent._private.style.opacity.value; - - parentOpacity = opacity * parentOpacity; - } - } - - return parentOpacity; - } - }, - - transparent: function(){ - var cy = this.cy(); - if( !cy.styleEnabled() ){ return false; } - - var ele = this[0]; - var hasCompoundNodes = ele.cy().hasCompoundNodes(); - - if( ele ){ - if( !hasCompoundNodes ){ - return ele._private.style.opacity.value === 0; - } else { - return ele.effectiveOpacity() === 0; - } - } - }, - - isFullAutoParent: function(){ - var cy = this.cy(); - if( !cy.styleEnabled() ){ return false; } - - var ele = this[0]; - - if( ele ){ - var autoW = ele._private.style['width'].value === 'auto'; - var autoH = ele._private.style['height'].value === 'auto'; - - return ele.isParent() && autoW && autoH; - } - }, - - backgrounding: function(){ - var cy = this.cy(); - if( !cy.styleEnabled() ){ return false; } - - var ele = this[0]; - - return ele._private.backgrounding ? true : false; - } - -}); - - -elesfn.bypass = elesfn.style = elesfn.css; -elesfn.renderedStyle = elesfn.renderedCss; -elesfn.removeBypass = elesfn.removeStyle = elesfn.removeCss; - -module.exports = elesfn; - -},{"../is":77}],27:[function(_dereq_,module,exports){ -'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.bind( params.event, data, handler ); - } - - // e.g. cy.nodes().select( handler ) - else if( args.length === 1 ){ - var handler = args[0]; - this.bind( params.event, handler ); - } - - // 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; - - if( params.overrideAble ){ - var overrideAble = params.overrideAble(ele); - - if( overrideAble !== undefined ){ - able = overrideAble; - - if( !overrideAble ){ return this; } // to save cycles assume not able for all on override - } - } - - if( able ){ - ele._private[params.field] = params.value; - - if( changed ){ - changedEles.push( ele ); - } - } - } - - var changedColl = this.spawn( changedEles ); - changedColl.updateStyle(); // change of state => possible change of style - changedColl.trigger( params.event ); - } - - return this; - }; -} - -function defineSwitchSet( params ){ - elesfn[ params.field ] = function(){ - var ele = this[0]; - - if( ele ){ - if( params.overrideField ){ - var val = params.overrideField(ele); - - if( val !== undefined ){ - return val; - } - } - - return ele._private[ params.field ]; - } - }; - - elesfn[ params.on ] = defineSwitchFunction({ - event: params.on, - field: params.field, - ableField: params.ableField, - overrideAble: params.overrideAble, - value: true - }); - - elesfn[ params.off ] = defineSwitchFunction({ - event: params.off, - field: params.field, - ableField: params.ableField, - overrideAble: params.overrideAble, - value: false - }); -} - -defineSwitchSet({ - field: 'locked', - overrideField: function(ele){ - return ele.cy().autolock() ? true : undefined; - }, - on: 'lock', - off: 'unlock' -}); - -defineSwitchSet({ - field: 'grabbable', - overrideField: function(ele){ - return ele.cy().autoungrabify() ? false : undefined; - }, - on: 'grabify', - off: 'ungrabify' -}); - -defineSwitchSet({ - field: 'selected', - ableField: 'selectable', - overrideAble: function(ele){ - return ele.cy().autounselectify() ? false : undefined; - }, - on: 'select', - off: 'unselect' -}); - -defineSwitchSet({ - field: 'selectable', - overrideField: function(ele){ - return ele.cy().autounselectify() ? false : undefined; - }, - on: 'selectify', - off: 'unselectify' -}); - -elesfn.deselect = elesfn.unselect; - -elesfn.grabbed = function(){ - var ele = this[0]; - if( ele ){ - return ele._private.grabbed; - } -}; - -defineSwitchSet({ - field: 'active', - on: 'activate', - off: 'unactivate' -}); - -elesfn.inactive = function(){ - var ele = this[0]; - if( ele ){ - return !ele._private.active; - } -}; - -module.exports = elesfn; - -},{}],28:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); -var is = _dereq_('../is'); - -var elesfn = {}; - -util.extend(elesfn, { - // get the root nodes in the DAG - roots: function( selector ){ - var eles = this; - var roots = []; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - if( !ele.isNode() ){ - continue; - } - - var hasEdgesPointingIn = ele.connectedEdges(function(){ - return this.data('target') === ele.id() && this.data('source') !== ele.id(); - }).length > 0; - - if( !hasEdgesPointingIn ){ - roots.push( ele ); - } - } - - return this.spawn( roots, { unique: true } ).filter( selector ); - }, - - // get the leaf nodes in the DAG - leaves: function( selector ){ - var eles = this; - var leaves = []; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - if( !ele.isNode() ){ - continue; - } - - var hasEdgesPointingOut = ele.connectedEdges(function(){ - return this.data('source') === ele.id() && this.data('target') !== ele.id(); - }).length > 0; - - if( !hasEdgesPointingOut ){ - leaves.push( ele ); - } - } - - return this.spawn( leaves, { unique: true } ).filter( selector ); - }, - - // normally called children in graph theory - // these nodes =edges=> outgoing nodes - outgoers: function( selector ){ - var eles = this; - var oEles = []; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - var eleId = ele.id(); - - if( !ele.isNode() ){ continue; } - - var edges = ele._private.edges; - for( var j = 0; j < edges.length; j++ ){ - var edge = edges[j]; - var srcId = edge._private.data.source; - var tgtId = edge._private.data.target; - - if( srcId === eleId && tgtId !== eleId ){ - oEles.push( edge ); - oEles.push( edge.target()[0] ); - } - } - } - - return this.spawn( oEles, { unique: true } ).filter( selector ); - }, - - // aka DAG descendants - successors: function( selector ){ - var eles = this; - var sEles = []; - var sElesIds = {}; - - for(;;){ - var outgoers = eles.outgoers(); - - if( outgoers.length === 0 ){ break; } // done if no outgoers left - - var newOutgoers = false; - for( var i = 0; i < outgoers.length; i++ ){ - var outgoer = outgoers[i]; - var outgoerId = outgoer.id(); - - if( !sElesIds[ outgoerId ] ){ - sElesIds[ outgoerId ] = true; - sEles.push( outgoer ); - newOutgoers = true; - } - } - - if( !newOutgoers ){ break; } // done if touched all outgoers already - - eles = outgoers; - } - - return this.spawn( sEles, { unique: true } ).filter( selector ); - }, - - // normally called parents in graph theory - // these nodes <=edges= incoming nodes - incomers: function( selector ){ - var eles = this; - var oEles = []; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - var eleId = ele.id(); - - if( !ele.isNode() ){ continue; } - - var edges = ele._private.edges; - for( var j = 0; j < edges.length; j++ ){ - var edge = edges[j]; - var srcId = edge._private.data.source; - var tgtId = edge._private.data.target; - - if( tgtId === eleId && srcId !== eleId ){ - oEles.push( edge ); - oEles.push( edge.source()[0] ); - } - } - } - - return this.spawn( oEles, { unique: true } ).filter( selector ); - }, - - // aka DAG ancestors - predecessors: function( selector ){ - var eles = this; - var pEles = []; - var pElesIds = {}; - - for(;;){ - var incomers = eles.incomers(); - - if( incomers.length === 0 ){ break; } // done if no incomers left - - var newIncomers = false; - for( var i = 0; i < incomers.length; i++ ){ - var incomer = incomers[i]; - var incomerId = incomer.id(); - - if( !pElesIds[ incomerId ] ){ - pElesIds[ incomerId ] = true; - pEles.push( incomer ); - newIncomers = true; - } - } - - if( !newIncomers ){ break; } // done if touched all incomers already - - eles = incomers; - } - - return this.spawn( pEles, { unique: true } ).filter( selector ); - } -}); - - -// Neighbourhood functions -////////////////////////// - -util.extend(elesfn, { - neighborhood: 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 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._private.source; - var tgt = edge._private.target; - var otherNode = node === src ? tgt : src; - - // need check in case of loop - if( otherNode.length > 0 ){ - elements.push( otherNode[0] ); // add node 1 hop away - } - - // add connected edge - elements.push( edge[0] ); - } - - } - - return ( this.spawn( elements, { unique: true } ) ).filter( selector ); - }, - - closedNeighborhood: function(selector){ - return this.neighborhood().add( this ).filter( selector ); - }, - - openNeighborhood: function(selector){ - return this.neighborhood( selector ); - } -}); - -// aliases -elesfn.neighbourhood = elesfn.neighborhood; -elesfn.closedNeighbourhood = elesfn.closedNeighborhood; -elesfn.openNeighbourhood = elesfn.openNeighborhood; - -// Edge functions -///////////////// - -util.extend(elesfn, { - source: function( selector ){ - var ele = this[0]; - var src; - - if( ele ){ - src = ele._private.source; - } - - return src && selector ? src.filter( selector ) : src; - }, - - target: function( selector ){ - var ele = this[0]; - var tgt; - - if( ele ){ - tgt = ele._private.target; - } - - return tgt && selector ? tgt.filter( selector ) : tgt; - }, - - sources: defineSourceFunction({ - attr: 'source' - }), - - targets: defineSourceFunction({ - attr: 'target' - }) -}); - -function defineSourceFunction( params ){ - return function( selector ){ - var sources = []; - - for( var i = 0; i < this.length; i++ ){ - var ele = this[i]; - var src = ele._private[ params.attr ]; - - if( src ){ - sources.push( src ); - } - } - - return this.spawn( sources, { unique: true } ).filter( selector ); - }; -} - -util.extend(elesfn, { - edgesWith: defineEdgesWithFunction(), - - edgesTo: defineEdgesWithFunction({ - thisIs: 'source' - }) -}); - -function defineEdgesWithFunction( params ){ - - return function edgesWithImpl( otherNodes ){ - var elements = []; - var cy = this._private.cy; - var p = params || {}; - - // get elements if a selector is specified - if( is.string(otherNodes) ){ - otherNodes = cy.$( otherNodes ); - } - - var thisIds = this._private.ids; - var otherIds = otherNodes._private.ids; - - for( var h = 0; h < otherNodes.length; h++ ){ - var edges = otherNodes[h]._private.edges; - - for( var i = 0; i < edges.length; i++ ){ - var edge = edges[i]; - var edgeData = edge._private.data; - var thisToOther = thisIds[ edgeData.source ] && otherIds[ edgeData.target ]; - var otherToThis = otherIds[ edgeData.source ] && thisIds[ edgeData.target ]; - var edgeConnectsThisAndOther = thisToOther || otherToThis; - - if( !edgeConnectsThisAndOther ){ continue; } - - if( p.thisIs ){ - if( p.thisIs === 'source' && !thisToOther ){ continue; } - - if( p.thisIs === 'target' && !otherToThis ){ continue; } - } - - elements.push( edge ); - } - } - - return this.spawn( elements, { unique: true } ); - }; -} - -util.extend(elesfn, { - connectedEdges: function( selector ){ - var retEles = []; - - var eles = this; - for( var i = 0; i < eles.length; i++ ){ - var node = eles[i]; - if( !node.isNode() ){ continue; } - - var edges = node._private.edges; - - for( var j = 0; j < edges.length; j++ ){ - var edge = edges[j]; - retEles.push( edge ); - } - } - - return this.spawn( retEles, { unique: true } ).filter( selector ); - }, - - connectedNodes: function( selector ){ - var retEles = []; - - var eles = this; - for( var i = 0; i < eles.length; i++ ){ - var edge = eles[i]; - if( !edge.isEdge() ){ continue; } - - retEles.push( edge.source()[0] ); - retEles.push( edge.target()[0] ); - } - - return this.spawn( retEles, { unique: true } ).filter( selector ); - }, - - parallelEdges: defineParallelEdgesFunction(), - - codirectedEdges: defineParallelEdgesFunction({ - codirected: true - }) -}); - -function defineParallelEdgesFunction(params){ - var defaults = { - codirected: false - }; - params = util.extend({}, defaults, params); - - return function( selector ){ - var elements = []; - var edges = this.edges(); - var p = params; - - // look at all the edges in the collection - for( var i = 0; i < edges.length; i++ ){ - var edge1 = edges[i]; - var src1 = edge1.source()[0]; - var srcid1 = src1.id(); - var tgt1 = edge1.target()[0]; - var tgtid1 = tgt1.id(); - 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 codirected = tgtid2 === tgtid1 && srcid2 === srcid1; - var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2; - - if( (p.codirected && codirected) || (!p.codirected && (codirected || oppdirected)) ){ - elements.push( edge2 ); - } - } - } - - return this.spawn( elements, { unique: true } ).filter( selector ); - }; - -} - -// Misc functions -///////////////// - -util.extend(elesfn, { - components: function(){ - var cy = this.cy(); - var visited = cy.collection(); - var unvisited = this.nodes(); - var components = []; - - var visitInComponent = function( node, component ){ - visited.merge( node ); - unvisited.unmerge( node ); - component.merge( node ); - }; - - do { - var component = cy.collection(); - components.push( component ); - - var root = unvisited[0]; - visitInComponent( root, component ); - - this.bfs({ - directed: false, - roots: root, - visit: function( i, depth, v, e, u ){ - visitInComponent( v, component ); - } - }); - - } while( unvisited.length > 0 ); - - return components.map(function( component ){ - return component.closedNeighborhood(); // add the edges - }); - } -}); - -module.exports = elesfn; - -},{"../is":77,"../util":94}],29:[function(_dereq_,module,exports){ -'use strict'; - -var zIndexSort = function( a, b ){ - var cy = a.cy(); - var a_p = a._private; - var b_p = b._private; - var zDiff = a_p.style['z-index'].value - b_p.style['z-index'].value; - var depthA = 0; - var depthB = 0; - var hasCompoundNodes = cy.hasCompoundNodes(); - var aIsNode = a_p.group === 'nodes'; - var aIsEdge = a_p.group === 'edges'; - var bIsNode = b_p.group === 'nodes'; - var bIsEdge = b_p.group === 'edges'; - - // no need to calculate element depth if there is no compound node - if( hasCompoundNodes ){ - depthA = a.zDepth(); - depthB = b.zDepth(); - } - - var depthDiff = depthA - depthB; - var sameDepth = depthDiff === 0; - - if( sameDepth ){ - - if( aIsNode && bIsEdge ){ - return 1; // 'a' is a node, it should be drawn later - - } else if( aIsEdge && bIsNode ){ - return -1; // 'a' is an edge, it should be drawn first - - } else { // both nodes or both edges - if( zDiff === 0 ){ // same z-index => compare indices in the core (order added to graph w/ last on top) - return a_p.index - b_p.index; - } else { - return zDiff; - } - } - - // elements on different level - } else { - return depthDiff; // deeper element should be drawn later - } - -}; - -module.exports = zIndexSort; - -},{}],30:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var util = _dereq_('../util'); -var Collection = _dereq_('../collection'); -var Element = _dereq_('../collection/element'); -var window = _dereq_('../window'); -var document = window ? window.document : null; -var NullRenderer = _dereq_('../extensions/renderer/null'); - -var corefn = { - add: function(opts){ - - var elements; - var cy = this; - - // add the elements - if( is.elementOrCollection(opts) ){ - var eles = opts; - - if( eles._private.cy === cy ){ // same instance => just restore - elements = eles.restore(); - - } else { // otherwise, copy from json - var jsons = []; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - jsons.push( ele.json() ); - } - - elements = new Collection( cy, jsons ); - } - } - - // specify an array of options - else if( is.array(opts) ){ - var jsons = opts; - - elements = new Collection(cy, jsons); - } - - // specify via opts.nodes and opts.edges - else if( is.plainObject(opts) && (is.array(opts.nodes) || is.array(opts.edges)) ){ - var elesByGroup = opts; - var jsons = []; - - var grs = ['nodes', 'edges']; - for( var i = 0, il = grs.length; i < il; i++ ){ - var group = grs[i]; - var elesArray = elesByGroup[group]; - - if( is.array(elesArray) ){ - - for( var j = 0, jl = elesArray.length; j < jl; j++ ){ - var json = util.extend( { group: group }, elesArray[j] ); - - jsons.push( json ); - } - } - } - - elements = new Collection(cy, jsons); - } - - // specify options for one element - else { - var json = opts; - elements = (new Element( cy, json )).collection(); - } - - return elements; - }, - - remove: function(collection){ - if( is.elementOrCollection(collection) ){ - collection = collection; - } else if( is.string(collection) ){ - var selector = collection; - collection = this.$( selector ); - } - - return collection.remove(); - }, - - load: function(elements, onload, ondone){ - var cy = this; - - cy.notifications(false); - - // remove old elements - var oldEles = cy.elements(); - if( oldEles.length > 0 ){ - oldEles.remove(); - } - - if( elements != null ){ - if( is.plainObject(elements) || is.array(elements) ){ - cy.add( elements ); - } - } - - cy.one('layoutready', function(e){ - cy.notifications(true); - cy.trigger(e); // we missed this event by turning notifications off, so pass it on - - cy.notify({ - type: 'load', - collection: cy.elements() - }); - - cy.one('load', onload); - cy.trigger('load'); - }).one('layoutstop', function(){ - cy.one('done', ondone); - cy.trigger('done'); - }); - - var layoutOpts = util.extend({}, cy._private.options.layout); - layoutOpts.eles = cy.$(); - - cy.layout( layoutOpts ); - - return this; - } -}; - -module.exports = corefn; - -},{"../collection":23,"../collection/element":19,"../extensions/renderer/null":73,"../is":77,"../util":94,"../window":100}],31:[function(_dereq_,module,exports){ -'use strict'; - -var define = _dereq_('../define'); -var util = _dereq_('../util'); -var is = _dereq_('../is'); - -var corefn = ({ - - // 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(), - - addToAnimationPool: function( eles ){ - var cy = this; - - if( !cy.styleEnabled() ){ return; } // save cycles when no style used - - cy._private.aniEles.merge( eles ); - }, - - stopAnimationLoop: function(){ - this._private.animationsRunning = false; - }, - - startAnimationLoop: function(){ - 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 globalAnimationStep(){ - if( !cy._private.animationsRunning ){ return; } - - util.requestAnimationFrame(function(now){ - handleElements(now); - globalAnimationStep(); - }); - } - - globalAnimationStep(); // first call - - function handleElements( now ){ - var eles = cy._private.aniEles; - var doneEles = []; - - function handleElement( ele, isCore ){ - var _p = ele._private; - var current = _p.animation.current; - var queue = _p.animation.queue; - var ranAnis = false; - - // if nothing currently animating, get something from the queue - if( current.length === 0 ){ - var next = queue.shift(); - - if( next ){ - current.push( next ); - } - } - - var callbacks = function( callbacks ){ - for( var j = callbacks.length - 1; j >= 0; j-- ){ - var cb = callbacks[j]; - - cb(); - } - - callbacks.splice( 0, callbacks.length ); - }; - - // step and remove if done - for( var i = current.length - 1; i >= 0; i-- ){ - var ani = current[i]; - var ani_p = ani._private; - - if( ani_p.stopped ){ - current.splice( i, 1 ); - - ani_p.hooked = false; - ani_p.playing = false; - ani_p.started = false; - - callbacks( ani_p.frames ); - - continue; - } - - if( !ani_p.playing && !ani_p.applying ){ continue; } - - // an apply() while playing shouldn't do anything - if( ani_p.playing && ani_p.applying ){ - ani_p.applying = false; - } - - if( !ani_p.started ){ - startAnimation( ele, ani, now ); - } - - step( ele, ani, now, isCore ); - - if( ani_p.applying ){ - ani_p.applying = false; - } - - callbacks( ani_p.frames ); - - if( ani.completed() ){ - current.splice(i, 1); - - ani_p.hooked = false; - ani_p.playing = false; - ani_p.started = false; - - callbacks( ani_p.completes ); - } - - ranAnis = true; - } - - if( !isCore && current.length === 0 && queue.length === 0 ){ - doneEles.push( ele ); - } - - return ranAnis; - } // handleElement - - // handle all eles - var ranEleAni = false; - for( var e = 0; e < eles.length; e++ ){ - var ele = eles[e]; - var handledThisEle = handleElement( ele ); - - ranEleAni = ranEleAni || handledThisEle; - } // each element - - var ranCoreAni = handleElement( cy, true ); - - // notify renderer - if( ranEleAni || ranCoreAni ){ - var toNotify; - - if( eles.length > 0 ){ - var updatedEles = eles.updateCompoundBounds(); - toNotify = updatedEles.length > 0 ? eles.add( updatedEles ) : eles; - } - - cy.notify({ - type: 'draw', - collection: toNotify - }); - } - - // remove elements from list of currently animating if its queues are empty - eles.unmerge( doneEles ); - - } // handleElements - - function startAnimation( self, ani, now ){ - var isCore = is.core( self ); - var isEles = !isCore; - var ele = self; - var style = cy._private.style; - var ani_p = ani._private; - - if( isEles ){ - var pos = ele._private.position; - - ani_p.startPosition = ani_p.startPosition || { - x: pos.x, - y: pos.y - }; - - ani_p.startStyle = ani_p.startStyle || style.getValueStyle( ele ); - } - - 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; - } - - function step( self, ani, now, isCore ){ - var style = cy._private.style; - var isEles = !isCore; - var _p = self._private; - var ani_p = ani._private; - var pEasing = ani_p.easing; - var startTime = ani_p.startTime; - - if( !ani_p.easingImpl ){ - - if( pEasing == null ){ // use default - ani_p.easingImpl = easings['linear']; - - } else { // then define w/ name - var easingVals; - - if( is.string( pEasing ) ){ - var easingProp = style.parse('transition-timing-function', pEasing); - - easingVals = easingProp.value; - - } else { // then assume preparsed array - easingVals = pEasing; - } - - var name, args; - - if( is.string( easingVals ) ){ - name = easingVals; - args = []; - } else { - name = easingVals[1]; - args = easingVals.slice(2).map(function(n){ return +n; }); - } - - if( args.length > 0 ){ // create with args - if( name === 'spring' ){ - args.push( ani_p.duration ); // need duration to generate spring - } - - ani_p.easingImpl = easings[ name ].apply( null, args ); - } else { // static impl by name - ani_p.easingImpl = easings[ name ]; - } - } - - } - - var easing = ani_p.easingImpl; - var percent; - - if( ani_p.duration === 0 ){ - percent = 1; - } else { - percent = (now - startTime) / ani_p.duration; - } - - if( ani_p.applying ){ - percent = ani_p.progress; - } - - if( percent < 0 ){ - percent = 0; - } else if( percent > 1 ){ - percent = 1; - } - - if( ani_p.delay == null ){ // then update - - var startPos = ani_p.startPosition; - var endPos = ani_p.position; - var pos = _p.position; - if( endPos && isEles ){ - if( valid( startPos.x, endPos.x ) ){ - pos.x = ease( startPos.x, endPos.x, percent, easing ); - } - - if( valid( startPos.y, endPos.y ) ){ - pos.y = ease( startPos.y, endPos.y, percent, easing ); - } - } - - 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 ); - } - - self.trigger('pan'); - } - - 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 ); - } - - self.trigger('zoom'); - } - - if( animatingPan || animatingZoom ){ - self.trigger('viewport'); - } - - var props = ani_p.style; - if( props && 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 easedVal = ease( start, end, percent, easing ); - - style.overrideBypass( self, name, easedVal ); - } // for props - - } // if - - } - - if( is.fn(ani_p.step) ){ - ani_p.step.apply( self, [ now ] ); - } - - ani_p.progress = percent; - - return percent; - } - - function valid(start, end){ - if( start == null || end == null ){ - return false; - } - - if( is.number(start) && is.number(end) ){ - return true; - } else if( (start) && (end) ){ - return true; - } - - return false; - } - - // assumes p0 = 0, p3 = 1 - function evalCubicBezier( p1, p2, t ){ - var one_t = 1 - t; - var tsq = t*t; - - return ( 3 * one_t * one_t * t * p1 ) + ( 3 * one_t * tsq * p2 ) + tsq * t; - } - - function cubicBezier( p1, p2 ){ - return function( start, end, percent ){ - return start + (end - start) * evalCubicBezier( p1, p2, percent ); - }; - } - - /* 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 - }; - - return { dx: state.v, dv: springAccelerationForState(state) }; - } - - 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); - - state.x = state.x + dxdt * dt; - state.v = state.v + dvdt * dt; - - return state; - } - - return function springRK4Factory (tension, friction, duration) { - - var initState = { - x: -1, - v: 0, - tension: null, - friction: null - }, - path = [0], - time_lapsed = 0, - tolerance = 1 / 10000, - DT = 16 / 1000, - have_duration, dt, last_state; - - tension = parseFloat(tension) || 500; - friction = parseFloat(friction) || 20; - duration = duration || null; - - initState.tension = tension; - initState.friction = friction; - - have_duration = duration !== null; - - /* 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; - } - - while (true) { - /* 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 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 ]; }; - }; - }()); - - var easings = { - 'linear': function( start, end, percent ){ - return start + (end - start) * percent; - }, - - // 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 ), - - // 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 ), - - // 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 ), - - // 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 ), - - // 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 ), - - // 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 ), - - - // user param easings... - - 'spring': function( tension, friction, duration ){ - var spring = generateSpringRK4( tension, friction, duration ); - - return function( start, end, percent ){ - return start + (end - start) * spring( percent ); - }; - }, - - 'cubic-bezier': function( x1, y1, x2, y2 ){ - return cubicBezier( x1, y1, x2, y2 ); - } - }; - - function ease( startProp, endProp, percent, easingFn ){ - if( percent < 0 ){ - percent = 0; - } else if( percent > 1 ){ - percent = 1; - } - - var start, end; - - if( startProp.pfValue != null || startProp.value != null ){ - start = startProp.pfValue != null ? startProp.pfValue : startProp.value; - } else { - start = startProp; - } - - if( endProp.pfValue != null || endProp.value != null ){ - end = endProp.pfValue != null ? endProp.pfValue : endProp.value; - } else { - end = endProp; - } - - if( is.number(start) && is.number(end) ){ - return easingFn( start, end, percent ); - - } else if( is.array(start) && is.array(end) ){ - var easedArr = []; - - for( var i = 0; i < end.length; i++ ){ - var si = start[i]; - var ei = end[i]; - - if( si != null && ei != null ){ - var val = easingFn(si, ei, percent); - - if( startProp.roundValue ){ val = Math.round( val ); } - - easedArr.push( val ); - } else { - easedArr.push( ei ); - } - } - - return easedArr; - } - - return undefined; - } - - } - -}); - -module.exports = corefn; - -},{"../define":41,"../is":77,"../util":94}],32:[function(_dereq_,module,exports){ -'use strict'; - -var define = _dereq_('../define'); - -var corefn = ({ - on: define.on(), // .on( events [, selector] [, data], handler) - one: define.on({ unbindSelfOnTrigger: true }), - once: define.on({ unbindAllBindersOnTrigger: true }), - off: define.off(), // .off( events [, selector] [, handler] ) - trigger: define.trigger() // .trigger( events [, extraParams] ) -}); - -define.eventAliasesOn( corefn ); - -module.exports = corefn; - -},{"../define":41}],33:[function(_dereq_,module,exports){ -'use strict'; - -var corefn = ({ - - png: function( options ){ - var renderer = this._private.renderer; - options = options || {}; - - return renderer.png( options ); - }, - - jpg: function( options ){ - var renderer = this._private.renderer; - options = options || {}; - - options.bg = options.bg || '#fff'; - - return renderer.jpg( options ); - } - -}); - -corefn.jpeg = corefn.jpg; - -module.exports = corefn; - -},{}],34:[function(_dereq_,module,exports){ -'use strict'; - -var window = _dereq_('../window'); -var util = _dereq_('../util'); -var Collection = _dereq_('../collection'); -var is = _dereq_('../is'); -var Promise = _dereq_('../promise'); -var define = _dereq_('../define'); - -var Core = function( opts ){ - if( !(this instanceof Core) ){ - return new Core(opts); - } - var cy = this; - - opts = util.extend({}, opts); - - var container = opts.container; - - // allow for passing a wrapped jquery object - // e.g. cytoscape({ container: $('#cy') }) - if( container && !is.htmlElement( container ) && is.htmlElement( container[0] ) ){ - container = container[0]; - } - - var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery - reg = reg || {}; - - if( reg && reg.cy ){ - reg.cy.destroy(); - - reg = {}; // old instance => replace reg completely - } - - var readies = reg.readies = reg.readies || []; - - if( container ){ container._cyreg = reg; } // make sure container assoc'd reg points to this cy - reg.cy = cy; - - var head = window !== undefined && container !== undefined && !opts.headless; - var options = opts; - options.layout = util.extend( { name: head ? 'grid' : 'null' }, options.layout ); - options.renderer = util.extend( { name: head ? 'canvas' : 'null' }, options.renderer ); - - var defVal = function( def, val, altVal ){ - if( val !== undefined ){ - return val; - } else if( altVal !== undefined ){ - return altVal; - } else { - return def; - } - }; - - var _p = this._private = { - container: container, // html dom ele container - ready: false, // whether ready has been triggered - initrender: false, // has initrender has been triggered - options: options, // cached options - elements: [], // array of elements - id2index: {}, // element id => index in elements array - listeners: [], // list of listeners - onRenders: [], // rendering listeners - aniEles: Collection(this), // elements being animated - scratch: {}, // scratch object for core - layout: null, - renderer: null, - notificationsEnabled: true, // whether notifications are sent to the renderer - minZoom: 1e-50, - maxZoom: 1e50, - zoomingEnabled: defVal(true, options.zoomingEnabled), - userZoomingEnabled: defVal(true, options.userZoomingEnabled), - panningEnabled: defVal(true, options.panningEnabled), - userPanningEnabled: defVal(true, options.userPanningEnabled), - boxSelectionEnabled: defVal(true, options.boxSelectionEnabled), - autolock: defVal(false, options.autolock, options.autolockNodes), - autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes), - autounselectify: defVal(false, options.autounselectify), - styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled, - zoom: is.number(options.zoom) ? options.zoom : 1, - pan: { - x: is.plainObject(options.pan) && is.number(options.pan.x) ? options.pan.x : 0, - y: is.plainObject(options.pan) && is.number(options.pan.y) ? options.pan.y : 0 - }, - animation: { // object for currently-running animations - current: [], - queue: [] - }, - hasCompoundNodes: false, - deferredExecQueue: [] - }; - - // set selection type - var selType = options.selectionType; - if( selType === undefined || (selType !== 'additive' && selType !== 'single') ){ - // then set default - - _p.selectionType = 'single'; - } else { - _p.selectionType = selType; - } - - // init zoom bounds - if( is.number(options.minZoom) && is.number(options.maxZoom) && options.minZoom < options.maxZoom ){ - _p.minZoom = options.minZoom; - _p.maxZoom = options.maxZoom; - } else if( is.number(options.minZoom) && options.maxZoom === undefined ){ - _p.minZoom = options.minZoom; - } else if( is.number(options.maxZoom) && options.minZoom === undefined ){ - _p.maxZoom = options.maxZoom; - } - - var loadExtData = function( next ){ - var anyIsPromise = false; - - for( var i = 0; i < extData.length; i++ ){ - var datum = extData[i]; - - if( is.promise(datum) ){ - anyIsPromise = true; - break; - } - } - - if( anyIsPromise ){ - return Promise.all( extData ).then( next ); // load all data asynchronously, then exec rest of init - } else { - next( extData ); // exec synchronously for convenience - } - }; - - // create the renderer - cy.initRenderer( util.extend({ - hideEdgesOnViewport: options.hideEdgesOnViewport, - hideLabelsOnViewport: options.hideLabelsOnViewport, - textureOnViewport: options.textureOnViewport, - wheelSensitivity: is.number(options.wheelSensitivity) && options.wheelSensitivity > 0 ? options.wheelSensitivity : 1, - motionBlur: options.motionBlur === undefined ? true : options.motionBlur, // on by default - motionBlurOpacity: options.motionBlurOpacity === undefined ? 0.05 : options.motionBlurOpacity, - pixelRatio: is.number(options.pixelRatio) && options.pixelRatio > 0 ? options.pixelRatio : (options.pixelRatio === 'auto' ? undefined : 1), - desktopTapThreshold: options.desktopTapThreshold === undefined ? 4 : options.desktopTapThreshold, - touchTapThreshold: options.touchTapThreshold === undefined ? 8 : options.touchTapThreshold - }, options.renderer) ); - - var extData = [ options.style, options.elements ]; - loadExtData(function( thens ){ - var initStyle = thens[0]; - var initEles = thens[1]; - - // init style - if( _p.styleEnabled ){ - cy.setStyle( initStyle ); - } - - // trigger the passed function for the `initrender` event - if( options.initrender ){ - cy.on('initrender', options.initrender); - cy.on('initrender', function(){ - _p.initrender = true; - }); - } - - // initial load - cy.load(initEles, function(){ // onready - cy.startAnimationLoop(); - _p.ready = true; - - // if a ready callback is specified as an option, the bind it - if( is.fn( options.ready ) ){ - cy.on('ready', options.ready); - } - - // bind all the ready handlers registered before creating this instance - for( var i = 0; i < readies.length; i++ ){ - var fn = readies[i]; - cy.on('ready', fn); - } - if( reg ){ reg.readies = []; } // clear b/c we've bound them all and don't want to keep it around in case a new core uses the same div etc - - cy.trigger('ready'); - }, options.done); - - }); -}; - -var corefn = Core.prototype; // short alias - -util.extend(corefn, { - instanceString: function(){ - return 'core'; - }, - - isReady: function(){ - return this._private.ready; - }, - - ready: function( fn ){ - if( this.isReady() ){ - this.trigger('ready', [], fn); // just calls fn as though triggered via ready event - } else { - this.on('ready', fn); - } - - return this; - }, - - initrender: function(){ - return this._private.initrender; - }, - - destroy: function(){ - var cy = this; - - cy.stopAnimationLoop(); - - 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] ); - } - } - - return cy; - }, - - getElementById: function( id ){ - var index = this._private.id2index[ id ]; - if( index !== undefined ){ - return this._private.elements[ index ]; - } - - // worst case, return an empty collection - return Collection( this ); - }, - - selectionType: function(){ - return this._private.selectionType; - }, - - hasCompoundNodes: function(){ - return this._private.hasCompoundNodes; - }, - - styleEnabled: function(){ - return this._private.styleEnabled; - }, - - addToPool: function( eles ){ - var elements = this._private.elements; - var id2index = this._private.id2index; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - - var id = ele._private.data.id; - var index = id2index[ id ]; - var alreadyInPool = index !== undefined; - - if( !alreadyInPool ){ - index = elements.length; - elements.push( ele ); - id2index[ id ] = index; - ele._private.index = index; - } - } - - return this; // chaining - }, - - removeFromPool: function( eles ){ - var elements = this._private.elements; - var id2index = this._private.id2index; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - - var id = ele._private.data.id; - var index = id2index[ id ]; - var inPool = index !== undefined; - - if( inPool ){ - this._private.id2index[ id ] = undefined; - elements.splice(index, 1); - - // adjust the index of all elements past this index - for( var j = index; j < elements.length; j++ ){ - var jid = elements[j]._private.data.id; - id2index[ jid ]--; - elements[j]._private.index--; - } - } - } - }, - - container: function(){ - return this._private.container; - }, - - options: function(){ - return util.copy( this._private.options ); - }, - - json: function( obj ){ - var cy = this; - var _p = cy._private; - - if( is.plainObject(obj) ){ // set - - cy.startBatch(); - - if( obj.elements ){ - var idInJson = {}; - - var updateEles = function( jsons, gr ){ - for( var i = 0; i < jsons.length; i++ ){ - var json = jsons[i]; - var id = json.data.id; - var ele = cy.getElementById( id ); - - idInJson[ id ] = true; - - if( ele.length !== 0 ){ // existing element should be updated - ele.json( json ); - } else { // otherwise should be added - if( gr ){ - cy.add( util.extend({ group: gr }, json) ); - } else { - cy.add( json ); - } - } - } - }; - - if( is.array(obj.elements) ){ // elements: [] - updateEles( obj.elements ); - - } else { // elements: { nodes: [], edges: [] } - var grs = ['nodes', 'edges']; - for( var i = 0; i < grs.length; i++ ){ - var gr = grs[i]; - var elements = obj.elements[ gr ]; - - if( is.array(elements) ){ - updateEles( elements, gr ); - } - } - } - - // elements not specified in json should be removed - cy.elements().stdFilter(function( ele ){ - return !idInJson[ ele.id() ]; - }).remove(); - } - - if( obj.style ){ - cy.style( obj.style ); - } - - if( obj.zoom != null && obj.zoom !== _p.zoom ){ - cy.zoom( obj.zoom ); - } - - if( obj.pan ){ - if( obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y ){ - cy.pan( obj.pan ); - } - } - - var fields = [ - 'minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', - 'panningEnabled', 'userPanningEnabled', - 'boxSelectionEnabled', - 'autolock', 'autoungrabify', 'autounselectify' - ]; - - for( var i = 0; i < fields.length; i++ ){ - var f = fields[i]; - - if( obj[f] != null ){ - cy[f]( obj[f] ); - } - } - - cy.endBatch(); - - return this; // chaining - } else if( obj === undefined ){ // get - var json = {}; - - json.elements = {}; - cy.elements().each(function(i, ele){ - var group = ele.group(); - - if( !json.elements[group] ){ - json.elements[group] = []; - } - - json.elements[group].push( ele.json() ); - }); - - if( this._private.styleEnabled ){ - json.style = cy.style().json(); - } - - json.zoomingEnabled = cy._private.zoomingEnabled; - json.userZoomingEnabled = cy._private.userZoomingEnabled; - json.zoom = cy._private.zoom; - json.minZoom = cy._private.minZoom; - json.maxZoom = cy._private.maxZoom; - json.panningEnabled = cy._private.panningEnabled; - json.userPanningEnabled = cy._private.userPanningEnabled; - json.pan = util.copy( cy._private.pan ); - json.boxSelectionEnabled = cy._private.boxSelectionEnabled; - json.renderer = util.copy( cy._private.options.renderer ); - json.hideEdgesOnViewport = cy._private.options.hideEdgesOnViewport; - json.hideLabelsOnViewport = cy._private.options.hideLabelsOnViewport; - json.textureOnViewport = cy._private.options.textureOnViewport; - json.wheelSensitivity = cy._private.options.wheelSensitivity; - json.motionBlur = cy._private.options.motionBlur; - - return json; - } - }, - - scratch: define.data({ - field: 'scratch', - bindingEvent: 'scratch', - allowBinding: true, - allowSetting: true, - settingEvent: 'scratch', - settingTriggersEvent: true, - triggerFnName: 'trigger', - allowGetting: true - }), - - removeScratch: define.removeData({ - field: 'scratch', - event: 'scratch', - triggerFnName: 'trigger', - triggerEvent: true - }) - -}); - -[ - _dereq_('./add-remove'), - _dereq_('./animation'), - _dereq_('./events'), - _dereq_('./export'), - _dereq_('./layout'), - _dereq_('./notification'), - _dereq_('./renderer'), - _dereq_('./search'), - _dereq_('./style'), - _dereq_('./viewport') -].forEach(function( props ){ - util.extend( corefn, props ); -}); - -module.exports = Core; - -},{"../collection":23,"../define":41,"../is":77,"../promise":80,"../util":94,"../window":100,"./add-remove":30,"./animation":31,"./events":32,"./export":33,"./layout":35,"./notification":36,"./renderer":37,"./search":38,"./style":39,"./viewport":40}],35:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); -var is = _dereq_('../is'); - -var corefn = ({ - - layout: function( params ){ - var layout = this._private.prevLayout = ( params == null ? this._private.prevLayout : this.makeLayout( params ) ); - - layout.run(); - - return this; // chaining - }, - - makeLayout: function( options ){ - var cy = this; - - if( options == null ){ - util.error('Layout options must be specified to make a layout'); - return; - } - - if( options.name == null ){ - util.error('A `name` must be specified to make a layout'); - return; - } - - var name = options.name; - var Layout = cy.extension('layout', name); - - if( Layout == null ){ - util.error('Can not apply layout: No such layout `' + name + '` found; did you include its JS file?'); - return; - } - - var eles; - if( is.string( options.eles ) ){ - eles = cy.$( options.eles ); - } else { - eles = options.eles != null ? options.eles : cy.$(); - } - - var layout = new Layout( util.extend({}, options, { - cy: cy, - eles: eles - }) ); - - return layout; - } - -}); - -corefn.createLayout = corefn.makeLayout; - -module.exports = corefn; - -},{"../is":77,"../util":94}],36:[function(_dereq_,module,exports){ -'use strict'; - -var corefn = ({ - notify: function( params ){ - var _p = this._private; - - if( _p.batchingNotify ){ - var bEles = _p.batchNotifyEles; - var bTypes = _p.batchNotifyTypes; - - if( params.collection ){ - bEles.merge( params.collection ); - } - - if( !bTypes.ids[ params.type ] ){ - bTypes.push( params.type ); - } - - return; // notifications are disabled during batching - } - - if( !_p.notificationsEnabled ){ return; } // exit on disabled - - var renderer = this.renderer(); - - renderer.notify(params); - }, - - notifications: function( bool ){ - var p = this._private; - - if( bool === undefined ){ - return p.notificationsEnabled; - } else { - p.notificationsEnabled = bool ? true : false; - } - }, - - noNotifications: function( callback ){ - this.notifications(false); - callback(); - this.notifications(true); - }, - - startBatch: function(){ - var _p = this._private; - - if( _p.batchCount == null ){ - _p.batchCount = 0; - } - - if( _p.batchCount === 0 ){ - _p.batchingStyle = _p.batchingNotify = true; - _p.batchStyleEles = this.collection(); - _p.batchNotifyEles = this.collection(); - _p.batchNotifyTypes = []; - - _p.batchNotifyTypes.ids = {}; - } - - _p.batchCount++; - - return this; - }, - - endBatch: function(){ - var _p = this._private; - - _p.batchCount--; - - if( _p.batchCount === 0 ){ - // update style for dirty eles - _p.batchingStyle = false; - _p.batchStyleEles.updateStyle(); - - // notify the renderer of queued eles and event types - _p.batchingNotify = false; - this.notify({ - type: _p.batchNotifyTypes, - collection: _p.batchNotifyEles - }); - } - - return this; - }, - - batch: function( callback ){ - this.startBatch(); - callback(); - this.endBatch(); - - return this; - }, - - // for backwards compatibility - batchData: function( map ){ - var cy = this; - - return this.batch(function(){ - for( var id in map ){ - var data = map[id]; - var ele = cy.getElementById( id ); - - ele.data( data ); - } - }); - } -}); - -module.exports = corefn; - -},{}],37:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); - -var corefn = ({ - - renderTo: function( context, zoom, pan, pxRatio ){ - var r = this._private.renderer; - - r.renderTo( context, zoom, pan, pxRatio ); - return this; - }, - - renderer: function(){ - return this._private.renderer; - }, - - forceRender: function(){ - this.notify({ - type: 'draw' - }); - - return this; - }, - - resize: function(){ - this.notify({ - type: 'resize' - }); - - this.trigger('resize'); - - return this; - }, - - initRenderer: function( 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; - } - - var rOpts = util.extend({}, options, { - cy: cy - }); - var renderer = cy._private.renderer = new RendererProto( rOpts ); - - renderer.init( rOpts ); - - }, - - triggerOnRender: function(){ - var cbs = this._private.onRenders; - - for( var i = 0; i < cbs.length; i++ ){ - var cb = cbs[i]; - - cb(); - } - - return this; - }, - - onRender: function( cb ){ - this._private.onRenders.push( cb ); - - return this; - }, - - offRender: function( fn ){ - var cbs = this._private.onRenders; - - if( fn == null ){ // unbind all - this._private.onRenders = []; - return this; - } - - for( var i = 0; i < cbs.length; i++ ){ // unbind specified - var cb = cbs[i]; - - if( fn === cb ){ - cbs.splice( i, 1 ); - break; - } - } - - return this; - } - -}); - -corefn.invalidateDimensions = corefn.resize; - -module.exports = corefn; - -},{"../util":94}],38:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var Collection = _dereq_('../collection'); - -var corefn = ({ - - // 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( eles, opts ){ - - if( is.string( eles ) ){ - return this.$( eles ); - - } else if( is.elementOrCollection( eles ) ){ - return eles.collection(); - - } else if( is.array( eles ) ){ - return Collection( this, eles, opts ); - } - - return Collection( this ); - }, - - nodes: function( selector ){ - var nodes = this.$(function(){ - return this.isNode(); - }); - - if( selector ){ - return nodes.filter( selector ); - } - - return nodes; - }, - - edges: function( selector ){ - var edges = this.$(function(){ - return this.isEdge(); - }); - - if( selector ){ - return edges.filter( selector ); - } - - return edges; - }, - - // search the graph like jQuery - $: function( selector ){ - var eles = new Collection( this, this._private.elements ); - - if( selector ){ - return eles.filter( selector ); - } - - return eles; - } - -}); - -// aliases -corefn.elements = corefn.filter = corefn.$; - -module.exports = corefn; - -},{"../collection":23,"../is":77}],39:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var Style = _dereq_('../style'); - -var corefn = ({ - - style: function( newStyle ){ - if( newStyle ){ - var s = this.setStyle( newStyle ); - - s.update(); - } - - return this._private.style; - }, - - setStyle: function( style ){ - var _p = this._private; - - 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 ); - } - - return _p.style; - } -}); - -module.exports = corefn; - -},{"../is":77,"../style":86}],40:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); - -var corefn = ({ - - autolock: function(bool){ - if( bool !== undefined ){ - this._private.autolock = bool ? true : false; - } else { - return this._private.autolock; - } - - return this; // chaining - }, - - autoungrabify: function(bool){ - if( bool !== undefined ){ - this._private.autoungrabify = bool ? true : false; - } else { - return this._private.autoungrabify; - } - - return this; // chaining - }, - - autounselectify: function(bool){ - if( bool !== undefined ){ - this._private.autounselectify = bool ? true : false; - } else { - return this._private.autounselectify; - } - - return this; // chaining - }, - - panningEnabled: function( bool ){ - if( bool !== undefined ){ - this._private.panningEnabled = bool ? true : false; - } else { - return this._private.panningEnabled; - } - - return this; // chaining - }, - - userPanningEnabled: function( bool ){ - if( bool !== undefined ){ - this._private.userPanningEnabled = bool ? true : false; - } else { - return this._private.userPanningEnabled; - } - - return this; // chaining - }, - - zoomingEnabled: function( bool ){ - if( bool !== undefined ){ - this._private.zoomingEnabled = bool ? true : false; - } else { - return this._private.zoomingEnabled; - } - - return this; // chaining - }, - - userZoomingEnabled: function( bool ){ - if( bool !== undefined ){ - this._private.userZoomingEnabled = bool ? true : false; - } else { - return this._private.userZoomingEnabled; - } - - return this; // chaining - }, - - boxSelectionEnabled: function( bool ){ - if( bool !== undefined ){ - this._private.boxSelectionEnabled = bool ? true : false; - } else { - return this._private.boxSelectionEnabled; - } - - return this; // chaining - }, - - pan: function(){ - var args = arguments; - var pan = this._private.pan; - var dim, val, dims, x, y; - - switch( args.length ){ - case 0: // .pan() - return pan; - - case 1: - - 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; - } - - dims = args[0]; - x = dims.x; - y = dims.y; - - if( is.number(x) ){ - pan.x = x; - } - - if( is.number(y) ){ - pan.y = y; - } - - this.trigger('pan viewport'); - } - break; - - case 2: // .pan('x', 100) - if( !this._private.panningEnabled ){ - return this; - } - - dim = args[0]; - val = args[1]; - - if( (dim === 'x' || dim === 'y') && is.number(val) ){ - pan[dim] = val; - } - - this.trigger('pan viewport'); - break; - - default: - break; // invalid - } - - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); - - return this; // chaining - }, - - panBy: function(params){ - var args = arguments; - var pan = this._private.pan; - var dim, val, dims, x, y; - - if( !this._private.panningEnabled ){ - return this; - } - - switch( args.length ){ - case 1: - - if( is.plainObject( args[0] ) ) { // .panBy({ x: 0, y: 100 }) - dims = args[0]; - x = dims.x; - y = dims.y; - - if( is.number(x) ){ - pan.x += x; - } - - if( is.number(y) ){ - pan.y += y; - } - - this.trigger('pan viewport'); - } - break; - - case 2: // .panBy('x', 100) - dim = args[0]; - val = args[1]; - - if( (dim === 'x' || dim === 'y') && is.number(val) ){ - pan[dim] += val; - } - - this.trigger('pan viewport'); - break; - - default: - break; // invalid - } - - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); - - return this; // chaining - }, - - fit: function( elements, padding ){ - var viewportState = this.getFitViewport( elements, padding ); - - if( viewportState ){ - var _p = this._private; - _p.zoom = viewportState.zoom; - _p.pan = viewportState.pan; - - this.trigger('pan zoom viewport'); - - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); - } - - return this; // chaining - }, - - getFitViewport: function( elements, padding ){ - if( is.number(elements) && padding === undefined ){ // elements is optional - padding = elements; - elements = undefined; - } - - if( !this._private.panningEnabled || !this._private.zoomingEnabled ){ - return; - } - - var bb; - - 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 - }; - - bb.w = bb.x2 - bb.x1; - bb.h = bb.y2 - bb.y1; - - } else if( !is.elementOrCollection(elements) ){ - elements = this.elements(); - } - - bb = bb || elements.boundingBox(); - - var w = this.width(); - var h = this.height(); - var zoom; - 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 pan = { // now pan to middle - x: (w - zoom*( bb.x1 + bb.x2 ))/2, - y: (h - zoom*( bb.y1 + bb.y2 ))/2 - }; - - return { - zoom: zoom, - pan: pan - }; - } - - return; - }, - - minZoom: function( zoom ){ - if( zoom === undefined ){ - return this._private.minZoom; - } else if( is.number(zoom) ){ - this._private.minZoom = zoom; - } - - return this; - }, - - maxZoom: function( zoom ){ - if( zoom === undefined ){ - return this._private.maxZoom; - } else if( is.number(zoom) ){ - this._private.maxZoom = zoom; - } - - return this; - }, - - zoom: function( params ){ - var pos; // in rendered px - var zoom; - - if( params === undefined ){ // then get the zoom - return this._private.zoom; - - } else if( is.number(params) ){ // then set the zoom - zoom = params; - - } else if( is.plainObject(params) ){ // then zoom about a point - zoom = params.level; - - if( params.position ){ - var p = params.position; - var pan = this._private.pan; - var z = this._private.zoom; - - pos = { // convert to rendered px - x: p.x * z + pan.x, - y: p.y * z + pan.y - }; - } else if( params.renderedPosition ){ - pos = params.renderedPosition; - } - - if( pos && !this._private.panningEnabled ){ - return this; // panning disabled - } - } - - if( !this._private.zoomingEnabled ){ - return this; // zooming disabled - } - - if( !is.number(zoom) || ( pos && (!is.number(pos.x) || !is.number(pos.y)) ) ){ - return this; // can't zoom with invalid params - } - - // crop zoom - zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom; - zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom; - - if( pos ){ // set zoom about position - var pan1 = this._private.pan; - var zoom1 = this._private.zoom; - var zoom2 = zoom; - - var pan2 = { - x: -zoom2/zoom1 * (pos.x - pan1.x) + pos.x, - y: -zoom2/zoom1 * (pos.y - pan1.y) + pos.y - }; - - this._private.zoom = zoom; - this._private.pan = pan2; - - var posChanged = pan1.x !== pan2.x || pan1.y !== pan2.y; - this.trigger(' zoom ' + (posChanged ? ' pan ' : '') + ' viewport ' ); - - } else { // just set the zoom - this._private.zoom = zoom; - this.trigger('zoom viewport'); - } - - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); - - return this; // chaining - }, - - viewport: function( 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; } - - if( zoomDefd ){ - var z = opts.zoom; - - if( z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled ){ - zoomFailed = true; - - } else { - _p.zoom = z; - - events.push('zoom'); - } - } - - if( panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled ){ - var p = opts.pan; - - if( is.number(p.x) ){ - _p.pan.x = p.x; - panFailed = false; - } - - if( is.number(p.y) ){ - _p.pan.y = p.y; - panFailed = false; - } - - if( !panFailed ){ - events.push('pan'); - } - } - - if( events.length > 0 ){ - events.push('viewport'); - this.trigger( events.join(' ') ); - - this.notify({ - type: 'viewport' - }); - } - - return this; // chaining - }, - - center: function( elements ){ - var pan = this.getCenterPan( elements ); - - if( pan ){ - this._private.pan = pan; - - this.trigger('pan viewport'); - - this.notify({ // notify the renderer that the viewport changed - type: 'viewport' - }); - } - - return this; // chaining - }, - - getCenterPan: function( elements, zoom ){ - if( !this._private.panningEnabled ){ - return; - } - - if( is.string(elements) ){ - var selector = elements; - elements = this.elements( selector ); - } else if( !is.elementOrCollection(elements) ){ - elements = this.elements(); - } - - var bb = elements.boundingBox(); - var w = this.width(); - var h = this.height(); - zoom = zoom === undefined ? this._private.zoom : zoom; - - var pan = { // middle - x: (w - zoom*( bb.x1 + bb.x2 ))/2, - y: (h - zoom*( bb.y1 + bb.y2 ))/2 - }; - - return pan; - }, - - reset: function(){ - if( !this._private.panningEnabled || !this._private.zoomingEnabled ){ - return this; - } - - this.viewport({ - pan: { x: 0, y: 0 }, - zoom: 1 - }); - - return this; // chaining - }, - - width: function(){ - var container = this._private.container; - - if( container ){ - return container.clientWidth; - } - - return 1; // fallback if no container (not 0 b/c can be used for dividing etc) - }, - - height: function(){ - var container = this._private.container; - - if( container ){ - return container.clientHeight; - } - - return 1; // fallback if no container (not 0 b/c can be used for dividing etc) - }, - - extent: function(){ - var pan = this._private.pan; - var zoom = this._private.zoom; - var rb = this.renderedExtent(); - - 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, - }; - - b.w = b.x2 - b.x1; - b.h = b.y2 - b.y1; - - return b; - }, - - renderedExtent: function(){ - var width = this.width(); - var height = this.height(); - - return { - x1: 0, - y1: 0, - x2: width, - y2: height, - w: width, - h: height - }; - } -}); - -// aliases -corefn.centre = corefn.center; - -// backwards compatibility -corefn.autolockNodes = corefn.autolock; -corefn.autoungrabifyNodes = corefn.autoungrabify; - -module.exports = corefn; - -},{"../is":77}],41:[function(_dereq_,module,exports){ -'use strict'; - -// use this module to cherry pick functions into your prototype -// (useful for functions shared between the core and collections, for example) - -// e.g. -// var foo = define.foo({ /* params... */ }) - -var util = _dereq_('./util'); -var is = _dereq_('./is'); -var Selector = _dereq_('./selector'); -var Promise = _dereq_('./promise'); -var Event = _dereq_('./event'); -var Animation = _dereq_('./animation'); - -var define = { - - // access data field - data: function( 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, - onSet: function( self ){}, - canSet: function( self ){ return true; } - }; - params = util.extend({}, defaults, params); - - 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; - if( 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 ){ - for( var i = 0, l = all.length; i < l; i++ ){ - if( p.canSet( all[i] ) ){ - all[i]._private[ p.field ][ name ] = value; - } - } - - // update mappers if asked - if( p.updateStyle ){ self.updateStyle(); } - - // call onSet callback - p.onSet( self ); - - if( p.settingTriggersEvent ){ - self[ p.triggerFnName ]( p.settingEvent ); - } - } - } - - // .data({ 'foo': 'bar' }) - } else if( p.allowSetting && is.plainObject(name) ){ // extend - var obj = name; - var k, v; - - for( k in obj ){ - v = obj[ k ]; - - var valid = !p.immutableKeys[k]; - if( valid ){ - for( var i = 0, l = all.length; i < l; i++ ){ - if( p.canSet( all[i] ) ){ - all[i]._private[ p.field ][ k ] = v; - } - } - } - } - - // update mappers if asked - if( p.updateStyle ){ self.updateStyle(); } - - // call onSet callback - p.onSet( self ); - - if( p.settingTriggersEvent ){ - self[ p.triggerFnName ]( p.settingEvent ); - } - - // .data(function(){ ... }) - } else if( p.allowBinding && is.fn(name) ){ // bind to event - var fn = name; - self.bind( p.bindingEvent, fn ); - - // .data() - } else if( p.allowGetting && name === undefined ){ // get whole object - var ret; - if( single ){ - ret = single._private[ p.field ]; - } - return ret; - } - - return self; // maintain chainability - }; // function - }, // data - - // remove data field - removeData: function( params ){ - var defaults = { - field: 'data', - event: 'data', - triggerFnName: 'trigger', - triggerEvent: false, - immutableKeys: {} // key => true if immutable - }; - params = util.extend({}, defaults, params); - - 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 - - // .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; - - 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; - } - } - } - - if( p.triggerEvent ){ - self[ p.triggerFnName ]( p.event ); - } - - // .removeData() - } else if( names === undefined ){ // then delete all keys - - for( var i_a = 0, l_a = all.length; i_a < l_a; i_a++ ){ - var _privateFields = all[ i_a ]._private[ p.field ]; - - for( var key in _privateFields ){ - var validKeyToDelete = !p.immutableKeys[ key ]; - - if( validKeyToDelete ){ - _privateFields[ key ] = undefined; - } - } - } - - if( p.triggerEvent ){ - self[ p.triggerFnName ]( p.event ); - } - } - - return self; // maintain chaining - }; // function - }, // removeData - - // event function reusable stuff - event: { - regex: /(\w+)(\.\w+)?/, // regex for matching event strings (e.g. "click.namespace") - optionalTypeRegex: /(\w+)?(\.\w+)?/, - falseCallback: function(){ return false; } - }, - - // event binding - on: function( params ){ - var defaults = { - unbindSelfOnTrigger: false, - unbindAllBindersOnTrigger: false - }; - params = util.extend({}, defaults, params); - - return function onImpl(events, selector, data, callback){ - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var eventsIsString = is.string(events); - var p = params; - - if( is.plainObject(selector) ){ // selector is actually data - callback = data; - data = selector; - selector = undefined; - } else if( is.fn(selector) || selector === false ){ // selector is actually callback - callback = selector; - data = undefined; - selector = undefined; - } - - if( is.fn(data) || data === false ){ // data is actually callback - callback = data; - data = undefined; - } - - // if there isn't a callback, we can't really do anything - // (can't speak for mapped events arg version) - if( !(is.fn(callback) || callback === false) && eventsIsString ){ - return self; // maintain chaining - } - - if( eventsIsString ){ // then convert to map - var map = {}; - map[ events ] = callback; - events = map; - } - - for( var evts in events ){ - callback = events[evts]; - if( callback === false ){ - callback = define.event.falseCallback; - } - - if( !is.fn(callback) ){ continue; } - - evts = evts.split(/\s+/); - for( var i = 0; i < evts.length; i++ ){ - var evt = evts[i]; - if( is.emptyString(evt) ){ continue; } - - var match = evt.match( define.event.regex ); // type[.namespace] - - if( match ){ - var type = match[1]; - var namespace = match[2] ? match[2] : undefined; - - var listener = { - callback: callback, // callback to run - data: data, // extra data in eventObj.data - delegated: selector ? true : false, // whether the evt is delegated - selector: selector, // the selector to match for delegated events - selObj: new Selector(selector), // cached selector object to save rebuilding - type: type, // the event type (e.g. 'click') - namespace: namespace, // the event namespace (e.g. ".foo") - unbindSelfOnTrigger: p.unbindSelfOnTrigger, - unbindAllBindersOnTrigger: p.unbindAllBindersOnTrigger, - binders: all // who bound together - }; - - for( var j = 0; j < all.length; j++ ){ - var _p = all[j]._private; - - _p.listeners = _p.listeners || []; - _p.listeners.push( listener ); - } - } - } // for events array - } // for events map - - return self; // maintain chaining - }; // function - }, // on - - eventAliasesOn: function( proto ){ - var p = proto; - - p.addListener = p.listen = p.bind = p.on; - p.removeListener = p.unlisten = p.unbind = p.off; - p.emit = p.trigger; - - // 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 ); - - return new Promise(function( resolve, reject ){ - var callback = function( e ){ - self.off.apply( self, offArgs ); - - resolve( e ); - }; - - var onArgs = args.concat([ callback ]); - var offArgs = onArgs.concat([]); - - self.on.apply( self, onArgs ); - }); - }; - }, - - off: function offImpl( params ){ - var defaults = { - }; - params = util.extend({}, defaults, params); - - return function(events, selector, callback){ - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var eventsIsString = is.string(events); - - if( arguments.length === 0 ){ // then unbind all - - for( var i = 0; i < all.length; i++ ){ - all[i]._private.listeners = []; - } - - return self; // maintain chaining - } - - if( is.fn(selector) || selector === false ){ // selector is actually callback - callback = selector; - selector = undefined; - } - - if( eventsIsString ){ // then convert to map - var map = {}; - map[ events ] = callback; - events = map; - } - - for( var evts in events ){ - callback = events[evts]; - - if( callback === false ){ - callback = define.event.falseCallback; - } - - evts = evts.split(/\s+/); - for( var h = 0; h < evts.length; h++ ){ - var evt = evts[h]; - if( is.emptyString(evt) ){ continue; } - - var match = evt.match( define.event.optionalTypeRegex ); // [type][.namespace] - if( match ){ - var type = match[1] ? match[1] : undefined; - var namespace = match[2] ? match[2] : undefined; - - for( var i = 0; i < all.length; i++ ){ // - var listeners = all[i]._private.listeners = all[i]._private.listeners || []; - - for( var j = 0; j < listeners.length; j++ ){ - var listener = listeners[j]; - var nsMatches = !namespace || namespace === listener.namespace; - var typeMatches = !type || listener.type === type; - var cbMatches = !callback || callback === listener.callback; - var listenerMatches = nsMatches && typeMatches && cbMatches; - - // delete listener if it matches - if( listenerMatches ){ - listeners.splice(j, 1); - j--; - } - } // for listeners - } // for all - } // if match - } // for events array - - } // for events map - - return self; // maintain chaining - }; // function - }, // off - - trigger: function( params ){ - var defaults = {}; - params = util.extend({}, defaults, params); - - return function triggerImpl(events, extraParams, fnToTrigger){ - var self = this; - var selfIsArrayLike = self.length !== undefined; - var all = selfIsArrayLike ? self : [self]; // put in array if not array-like - var eventsIsString = is.string(events); - var eventsIsObject = is.plainObject(events); - var eventsIsEvent = is.event(events); - var cy = this._private.cy || ( is.core(this) ? this : null ); - var hasCompounds = cy ? cy.hasCompoundNodes() : false; - - if( eventsIsString ){ // then make a plain event object for each event name - var evts = events.split(/\s+/); - events = []; - - for( var i = 0; i < evts.length; i++ ){ - var evt = evts[i]; - if( is.emptyString(evt) ){ continue; } - - var match = evt.match( define.event.regex ); // type[.namespace] - var type = match[1]; - var namespace = match[2] ? match[2] : undefined; - - events.push( { - type: type, - namespace: namespace - } ); - } - } else if( eventsIsObject ){ // put in length 1 array - var eventArgObj = events; - - events = [ eventArgObj ]; - } - - if( extraParams ){ - if( !is.array(extraParams) ){ // make sure extra params are in an array if specified - extraParams = [ extraParams ]; - } - } else { // otherwise, we've got nothing - extraParams = []; - } - - for( var i = 0; i < events.length; i++ ){ // trigger each event in order - var evtObj = events[i]; - - for( var j = 0; j < all.length; j++ ){ // for each - var triggerer = all[j]; - var listeners = triggerer._private.listeners = triggerer._private.listeners || []; - var triggererIsElement = is.element(triggerer); - var bubbleUp = triggererIsElement || params.layout; - - // create the event for this element from the event object - var evt; - - if( eventsIsEvent ){ // then just get the object - evt = evtObj; - - evt.cyTarget = evt.cyTarget || triggerer; - evt.cy = evt.cy || cy; - - } else { // then we have to make one - evt = new Event( evtObj, { - cyTarget: triggerer, - cy: cy, - namespace: evtObj.namespace - } ); - } - - // if a layout was specified, then put it in the typed event - if( evtObj.layout ){ - evt.layout = evtObj.layout; - } - - // if triggered by layout, put in event - if( params.layout ){ - evt.layout = triggerer; - } - - // create a rendered position based on the passed position - if( evt.cyPosition ){ - var pos = evt.cyPosition; - var zoom = cy.zoom(); - var pan = cy.pan(); - - evt.cyRenderedPosition = { - x: pos.x * zoom + pan.x, - y: pos.y * zoom + pan.y - }; - } - - if( fnToTrigger ){ // then override the listeners list with just the one we specified - listeners = [{ - namespace: evt.namespace, - type: evt.type, - callback: fnToTrigger - }]; - } - - for( var k = 0; k < listeners.length; k++ ){ // check each listener - var lis = listeners[k]; - var nsMatches = !lis.namespace || lis.namespace === evt.namespace; - var typeMatches = lis.type === evt.type; - var targetMatches = lis.delegated ? ( triggerer !== evt.cyTarget && is.element(evt.cyTarget) && lis.selObj.matches(evt.cyTarget) ) : (true); // we're not going to validate the hierarchy; that's too expensive - var listenerMatches = nsMatches && typeMatches && targetMatches; - - if( listenerMatches ){ // then trigger it - var args = [ evt ]; - args = args.concat( extraParams ); // add extra params to args list - - if( lis.data ){ // add on data plugged into binding - evt.data = lis.data; - } else { // or clear it in case the event obj is reused - evt.data = undefined; - } - - if( lis.unbindSelfOnTrigger || lis.unbindAllBindersOnTrigger ){ // then remove listener - listeners.splice(k, 1); - k--; - } - - if( lis.unbindAllBindersOnTrigger ){ // then delete the listener for all binders - var binders = lis.binders; - for( var l = 0; l < binders.length; l++ ){ - var binder = binders[l]; - if( !binder || binder === triggerer ){ continue; } // already handled triggerer or we can't handle it - - var binderListeners = binder._private.listeners; - for( var m = 0; m < binderListeners.length; m++ ){ - var binderListener = binderListeners[m]; - - if( binderListener === lis ){ // delete listener from list - binderListeners.splice(m, 1); - m--; - } - } - } - } - - // run the callback - var context = lis.delegated ? evt.cyTarget : triggerer; - var ret = lis.callback.apply( context, args ); - - if( ret === false || evt.isPropagationStopped() ){ - // then don't bubble - bubbleUp = false; - - if( ret === false ){ - // returning false is a shorthand for stopping propagation and preventing the def. action - evt.stopPropagation(); - evt.preventDefault(); - } - } - } // if listener matches - } // for each listener - - // bubble up event for elements - if( bubbleUp ){ - var parent = hasCompounds ? triggerer._private.parent : null; - var hasParent = parent != null && parent.length !== 0; - - if( hasParent ){ // then bubble up to parent - parent = parent[0]; - parent.trigger(evt); - } else { // otherwise, bubble up to the core - cy.trigger(evt); - } - } - - } // for each of all - } // for each event - - return self; // maintain chaining - }; // function - }, // trigger - - animated: function( fnParams ){ - var defaults = {}; - fnParams = util.extend({}, defaults, fnParams); - - 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( !cy.styleEnabled() ){ return false; } - - var ele = all[0]; - - if( ele ){ - return ele._private.animation.current.length > 0; - } - }; - }, // animated - - clearQueue: function( fnParams ){ - var defaults = {}; - fnParams = util.extend({}, defaults, fnParams); - - 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; - - if( !cy.styleEnabled() ){ return this; } - - for( var i = 0; i < all.length; i++ ){ - var ele = all[i]; - ele._private.animation.queue = []; - } - - return this; - }; - }, // clearQueue - - delay: function( fnParams ){ - var defaults = {}; - fnParams = util.extend({}, defaults, fnParams); - - return function delayImpl( time, complete ){ - var cy = this._private.cy || this; - - if( !cy.styleEnabled() ){ return this; } - - return this.animate({ - delay: time, - duration: time, - complete: complete - }); - }; - }, // delay - - delayAnimation: function( fnParams ){ - var defaults = {}; - fnParams = util.extend({}, defaults, fnParams); - - return function delayAnimationImpl( time, complete ){ - var cy = this._private.cy || this; - - if( !cy.styleEnabled() ){ return this; } - - return this.animation({ - delay: time, - duration: time, - complete: complete - }); - }; - }, // delay - - animation: function( fnParams ){ - var defaults = {}; - fnParams = util.extend({}, defaults, fnParams); - - 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; - - if( !cy.styleEnabled() ){ return this; } - - var style = cy.style(); - - properties = util.extend( {}, properties, params ); - - if( properties.duration === undefined ){ - properties.duration = 400; - } - - switch( properties.duration ){ - case 'slow': - properties.duration = 600; - break; - case 'fast': - properties.duration = 200; - break; - } - - var propertiesEmpty = true; - if( properties ){ for( var i in properties ){ // jshint ignore:line - propertiesEmpty = false; - break; - } } - - if( propertiesEmpty ){ - return new Animation( all[0], properties ); // nothing to animate - } - - if( isEles ){ - properties.style = style.getPropsList( properties.style || properties.css ); - - properties.css = undefined; - } - - if( properties.renderedPosition && isEles ){ - var rpos = properties.renderedPosition; - var pan = cy.pan(); - var zoom = cy.zoom(); - - properties.position = { - x: ( rpos.x - pan.x ) /zoom, - y: ( rpos.y - pan.y ) /zoom - }; - } - - // override pan w/ panBy if set - if( properties.panBy && isCore ){ - var panBy = properties.panBy; - var cyPan = cy.pan(); - - 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( center && isCore ){ - var centerPan = cy.getCenterPan( center.eles, properties.zoom ); - - if( centerPan ){ - properties.pan = centerPan; - } - } - - // override pan & zoom w/ fit if set - if( properties.fit && isCore ){ - var fit = properties.fit; - var fitVp = cy.getFitViewport( fit.eles || fit.boundingBox, fit.padding ); - - if( fitVp ){ - properties.pan = fitVp.pan; - properties.zoom = fitVp.zoom; - } - } - - return new Animation( all[0], properties ); - }; - }, // animate - - animate: function( fnParams ){ - var defaults = {}; - fnParams = util.extend({}, defaults, fnParams); - - 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; - - if( !cy.styleEnabled() ){ return this; } - - if( params ){ - properties = util.extend( {}, properties, params ); - } - - // 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); - - var ani = ele.animation( properties, (queue ? { queue: true } : undefined) ); - - ani.play(); - } - - return this; // chaining - }; - }, // animate - - stop: function( fnParams ){ - var defaults = {}; - fnParams = util.extend({}, defaults, fnParams); - - 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( !cy.styleEnabled() ){ return this; } - - for( var i = 0; i < all.length; i++ ){ - var ele = all[i]; - var _p = ele._private; - var anis = _p.animation.current; - - for( var j = 0; j < anis.length; j++ ){ - var ani = anis[j]; - var ani_p = ani._private; - - if( jumpToEnd ){ - // next iteration of the animation loop, the animation - // will go straight to the end and be removed - ani_p.duration = 0; - } - } - - // 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({ - collection: this, - type: 'draw' - }); - - return this; - }; - } // stop - -}; // define - -module.exports = define; - -},{"./animation":1,"./event":42,"./is":77,"./promise":80,"./selector":81,"./util":94}],42:[function(_dereq_,module,exports){ -'use strict'; - -// ref -// https://github.com/jquery/jquery/blob/master/src/event.js - -var Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof Event) ) { - return new Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - 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; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - // util.extend( this, props ); - - // more efficient to manually copy fields we use - this.type = props.type !== undefined ? props.type : this.type; - this.cy = props.cy; - this.cyTarget = props.cyTarget; - this.cyPosition = props.cyPosition; - this.cyRenderedPosition = props.cyRenderedPosition; - this.namespace = props.namespace; - this.layout = props.layout; - this.data = props.data; - this.message = props.message; - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); -}; - -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(){ - return 'event'; - }, - - preventDefault: function() { - 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() { - 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() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -module.exports = Event; - -},{}],43:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('./util'); -var define = _dereq_('./define'); -var Collection = _dereq_('./collection'); -var Core = _dereq_('./core'); -var incExts = _dereq_('./extensions'); -var is = _dereq_('./is'); - -// registered extensions to cytoscape, indexed by name -var extensions = {}; - -// registered modules for extensions, indexed by name -var modules = {}; - -function setExtension( type, name, registrant ){ - - var ext = registrant; - - if( type === 'core' ){ - Core.prototype[ name ] = registrant; - - } else if( type === 'collection' ){ - Collection.prototype[ name ] = registrant; - - } else if( type === 'layout' ){ - // fill in missing layout functions in the prototype - - var Layout = function( options ){ - this.options = options; - - registrant.call( this, options ); - - // make sure layout has _private for use w/ std apis like .on() - if( !is.plainObject(this._private) ){ - this._private = {}; - } - - this._private.cy = options.cy; - this._private.listeners = []; - }; - - var layoutProto = Layout.prototype = Object.create( registrant.prototype ); - - var optLayoutFns = []; - - for( var i = 0; i < optLayoutFns.length; i++ ){ - var fnName = optLayoutFns[i]; - - layoutProto[fnName] = layoutProto[fnName] || function(){ return this; }; - } - - // 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( !layoutProto.stop ){ - layoutProto.stop = function(){ - var opts = this.options; - - if( opts && opts.animate ){ - var anis = this.animations; - for( var i = 0; i < anis.length; i++ ){ - anis[i].stop(); - } - } - - this.trigger('layoutstop'); - - return this; - }; - } - - if( !layoutProto.destroy ){ - layoutProto.destroy = function(){ - return this; - }; - } - - layoutProto.on = define.on({ layout: true }); - layoutProto.one = define.on({ layout: true, unbindSelfOnTrigger: true }); - layoutProto.once = define.on({ layout: true, unbindAllBindersOnTrigger: true }); - layoutProto.off = define.off({ layout: true }); - layoutProto.trigger = define.trigger({ layout: true }); - - define.eventAliasesOn( layoutProto ); - - ext = Layout; // replace with our wrapped layout - - } else if( type === 'renderer' && name !== 'null' && name !== 'base' ){ - // user registered renderers inherit from base - - var bProto = getExtension( 'renderer', 'base' ).prototype; - var rProto = registrant.prototype; - - for( var pName in bProto ){ - var pVal = bProto[ pName ]; - var existsInR = rProto[ pName ] != null; - - if( existsInR ){ - util.error('Can not register renderer `' + name + '` since it overrides `' + pName + '` in its prototype'); - return; - } - - rProto[ pName ] = pVal; // take impl from base - } - - bProto.clientFunctions.forEach(function( name ){ - rProto[ name ] = rProto[ name ] || function(){ - util.error('Renderer does not implement `renderer.' + name + '()` on its prototype'); - }; - }); - - } - - 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 ] - }); -} - -var extension = function(){ - // e.g. extension('renderer', 'svg') - if( arguments.length === 2 ){ - return getExtension.apply(null, arguments); - } - - // e.g. extension('renderer', 'svg', { ... }) - else if( arguments.length === 3 ){ - return setExtension.apply(null, arguments); - } - - // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse') - else if( arguments.length === 4 ){ - return getModule.apply(null, arguments); - } - - // 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; - -},{"./collection":23,"./core":34,"./define":41,"./extensions":44,"./is":77,"./util":94}],44:[function(_dereq_,module,exports){ -'use strict'; - -module.exports = [ - { - type: 'layout', - extensions: _dereq_('./layout') - }, - - { - type: 'renderer', - extensions: _dereq_('./renderer') - } -]; - -},{"./layout":50,"./renderer":72}],45:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); -var math = _dereq_('../../math'); -var is = _dereq_('../../is'); - -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 - 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 - ready: undefined, // callback on layoutready - stop: undefined // callback on layoutstop -}; - -function BreadthFirstLayout( options ){ - this.options = util.extend({}, defaults, options); -} - -BreadthFirstLayout.prototype.run = function(){ - var params = this.options; - var options = params; - - var cy = params.cy; - var eles = options.eles; - var nodes = eles.nodes().not(':parent'); - var graph = eles; - - var bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : { - x1: 0, y1: 0, w: cy.width(), h: cy.height() - } ); - - var roots; - if( is.elementOrCollection(options.roots) ){ - roots = options.roots; - } else if( is.array(options.roots) ){ - var rootsArray = []; - - for( var i = 0; i < options.roots.length; i++ ){ - var id = options.roots[i]; - var ele = cy.getElementById( id ); - rootsArray.push( ele ); - } - - 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; - - while( unhandledNodes.length > 0 ){ - var currComp = cy.collection(); - - eles.bfs({ - roots: unhandledNodes[0], - visit: function(i, depth, node, edge, pNode){ - currComp = currComp.add( node ); - }, - directed: false - }); - - unhandledNodes = unhandledNodes.not( currComp ); - components.push( currComp ); - } - - roots = cy.collection(); - for( var i = 0; i < components.length; i++ ){ - var comp = components[i]; - var maxDegree = comp.maxDegree( false ); - var compRoots = comp.filter(function(){ - return this.degree(false) === maxDegree; - }); - - roots = roots.add( compRoots ); - } - - } - } - - - var depths = []; - var foundByBfs = {}; - var id2depth = {}; - var prevNode = {}; - var prevEdge = {}; - var successors = {}; - - // find the depths of the nodes - graph.bfs({ - roots: roots, - directed: options.directed, - visit: function(i, depth, node, edge, pNode){ - var ele = this[0]; - var id = ele.id(); - - if( !depths[depth] ){ - depths[depth] = []; - } - - depths[depth].push( ele ); - foundByBfs[ id ] = true; - id2depth[ id ] = depth; - prevNode[ id ] = pNode; - prevEdge[ id ] = edge; - - if( pNode ){ - var prevId = pNode.id(); - var succ = successors[ prevId ] = successors[ prevId ] || []; - - succ.push( node ); - } - } - }); - - // check for nodes not found by bfs - var orphanNodes = []; - for( var i = 0; i < nodes.length; i++ ){ - var ele = nodes[i]; - - if( foundByBfs[ ele.id() ] ){ - continue; - } else { - orphanNodes.push( ele ); - } - } - - // 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; - - for( var i = 0; i < neighbors.length; i++ ){ - var depth = id2depth[ neighbors[i].id() ]; - - if( depth !== undefined ){ - depths[depth].push( node ); - assignedDepth = true; - break; - } - } - - if( !assignedDepth ){ - orphanNodes.push( node ); - } - - checks++; - } - - // assign orphan nodes that are still left to the depth of their subgraph - while( orphanNodes.length !== 0 ){ - var node = orphanNodes.shift(); - //var subgraph = graph.bfs( node ).path; - var assignedDepth = false; - - // for( var i = 0; i < subgraph.length; i++ ){ - // var depth = id2depth[ subgraph[i].id() ]; - - // if( depth !== undefined ){ - // depths[depth].push( node ); - // assignedDepth = true; - // break; - // } - // } - - 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 ); - } - } - - // assign the nodes a depth and index - var assignDepthsToEles = function(){ - for( var i = 0; i < depths.length; i++ ){ - var eles = depths[i]; - - for( var j = 0; j < eles.length; j++ ){ - var ele = eles[j]; - - ele._private.scratch.breadthfirst = { - depth: i, - index: j - }; - } - } - }; - assignDepthsToEles(); - - - var intersectsDepth = function( node ){ // returns true if has edges pointing in from a higher depth - var edges = node.connectedEdges(function(){ - return this.data('target') === node.id(); - }); - var thisInfo = node._private.scratch.breadthfirst; - var highestDepthOfOther = 0; - var highestOther; - for( var i = 0; i < edges.length; i++ ){ - var edge = edges[i]; - 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 highestOther; - }; - - // make maximal if so set by adjusting depths - for( var adj = 0; adj < options.maximalAdjustments; adj++ ){ - - var nDepths = depths.length; - var elesToMove = []; - for( var i = 0; i < nDepths; i++ ){ - var depth = depths[i]; - - var nDepth = depth.length; - for( var j = 0; j < nDepth; j++ ){ - var ele = depth[j]; - var info = ele._private.scratch.breadthfirst; - var intEle = intersectsDepth(ele); - - if( intEle ){ - info.intEle = intEle; - elesToMove.push( ele ); - } - } - } - - for( var i = 0; i < elesToMove.length; i++ ){ - var ele = elesToMove[i]; - var info = ele._private.scratch.breadthfirst; - var intEle = info.intEle; - var intInfo = intEle._private.scratch.breadthfirst; - - depths[ info.depth ].splice( info.index, 1 ); // remove from old depth & index - - // add to end of new depth - var newDepth = intInfo.depth + 1; - while( newDepth > depths.length - 1 ){ - depths.push([]); - } - depths[ newDepth ].push( ele ); - - info.depth = newDepth; - info.index = depths[newDepth].length - 1; - } - - assignDepthsToEles(); - } - - // find min distance we need to leave between nodes - var minDistance = 0; - if( options.avoidOverlap ){ - for( var i = 0; i < nodes.length; i++ ){ - var n = nodes[i]; - var nbb = n.boundingBox(); - var w = nbb.w; - var h = nbb.h; - - minDistance = Math.max(minDistance, w, h); - } - minDistance *= options.spacingFactor; // just to have some nice spacing - } - - // get the weighted percent for an element based on its connectivity to other levels - var cachedWeightedPercent = {}; - var getWeightedPercent = function( ele ){ - if( cachedWeightedPercent[ ele.id() ] ){ - return cachedWeightedPercent[ ele.id() ]; - } - - var eleDepth = ele._private.scratch.breadthfirst.depth; - var neighbors = ele.neighborhood().nodes().not(':parent'); - var percent = 0; - var samples = 0; - - for( var i = 0; i < neighbors.length; i++ ){ - var neighbor = neighbors[i]; - var bf = neighbor._private.scratch.breadthfirst; - var index = bf.index; - var depth = bf.depth; - var nDepth = depths[depth].length; - - if( eleDepth > depth || eleDepth === 0 ){ // only get influenced by elements above - percent += index / nDepth; - samples++; - } - } - - samples = Math.max(1, samples); - percent = percent / samples; - - if( samples === 0 ){ // so lone nodes have a "don't care" state in sorting - percent = undefined; - } - - cachedWeightedPercent[ ele.id() ] = percent; - return percent; - }; - - - // rearrange the indices in each depth level based on connectivity - - var sortFn = function(a, b){ - var apct = getWeightedPercent( a ); - var bpct = getWeightedPercent( b ); - - return apct - bpct; - }; - - 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 - - for( var i = 0; i < depths.length; i++ ){ - depths[i] = depths[i].sort( sortFn ); - } - assignDepthsToEles(); // and update - - } - - var biggestDepthSize = 0; - for( var i = 0; i < depths.length; i++ ){ - biggestDepthSize = Math.max( depths[i].length, biggestDepthSize ); - } - - var center = { - x: bb.x1 + bb.w/2, - y: bb.x1 + bb.h/2 - }; - - var getPosition = function( 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 ){ - - var epos = { - x: center.x + (index + 1 - (depthSize + 1)/2) * distanceX, - y: (depth + 1) * distanceY - }; - - if( isBottomDepth ){ - return epos; - } - - // var succs = successors[ ele.id() ]; - // if( succs ){ - // epos.x = 0; - // - // for( var i = 0 ; i < succs.length; i++ ){ - // var spos = pos[ succs[i].id() ]; - // - // epos.x += spos.x; - // } - // - // epos.x /= succs.length; - // } else { - // //debugger; - // } - - 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; - - 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 - }; - } - } - - }; - - // get positions in reverse depth order - var pos = {}; - for( var i = depths.length - 1; i >=0; i-- ){ - var depth = depths[i]; - - for( var j = 0; j < depth.length; j++ ){ - var node = depth[j]; - - pos[ node.id() ] = getPosition( node, i === depths.length - 1 ); - } - } - - nodes.layoutPositions(this, options, function(){ - return pos[ this.id() ]; - }); - - return this; // chaining -}; - -module.exports = BreadthFirstLayout; - -},{"../../is":77,"../../math":79,"../../util":94}],46:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); -var math = _dereq_('../../math'); -var is = _dereq_('../../is'); - -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 - 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 - ready: undefined, // callback on layoutready - stop: undefined // callback on layoutstop -}; - -function CircleLayout( options ){ - this.options = util.extend({}, defaults, options); -} - -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 ); - } - - var bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : { - x1: 0, y1: 0, w: cy.width(), h: cy.height() - } ); - - var center = { - x: bb.x1 + bb.w/2, - y: bb.y1 + bb.h/2 - }; - - 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; - - var minDistance = 0; - for( var i = 0; i < nodes.length; i++ ){ - var n = nodes[i]; - var nbb = n.boundingBox(); - var w = nbb.w; - var h = nbb.h; - - minDistance = Math.max(minDistance, w, h); - } - - 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; - } - - // 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 - - 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( i, ele ){ - 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 = CircleLayout; - -},{"../../is":77,"../../math":79,"../../util":94}],47:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); -var math = _dereq_('../../math'); - -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 - height: undefined, // height of layout area (overrides container height) - width: undefined, // width of layout area (overrides container width) - concentric: function(node){ // returns numeric value for each node, placing higher nodes in levels towards the centre - return node.degree(); - }, - levelWidth: function(nodes){ // the variation 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 - ready: undefined, // callback on layoutready - stop: undefined // callback on layoutstop -}; - -function ConcentricLayout( options ){ - this.options = util.extend({}, defaults, options); -} - -ConcentricLayout.prototype.run = function(){ - var params = this.options; - var options = params; - - var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise; - - var cy = params.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 center = { - x: bb.x1 + bb.w/2, - y: bb.y1 + bb.h/2 - }; - - var nodeValues = []; // { node, value } - var theta = options.startAngle; - var maxNodeSize = 0; - - for( var i = 0; i < nodes.length; i++ ){ - var node = nodes[i]; - var value; - - // calculate the node value - value = options.concentric.apply(node, [ node ]); - nodeValues.push({ - value: value, - node: node - }); - - // for style mapping - node._private.scratch.concentric = value; - } - - // in case we used the `concentric` in style - nodes.updateStyle(); - - // calculate max size now based on potentially updated mappers - for( var i = 0; i < nodes.length; i++ ){ - var node = nodes[i]; - var nbb = node.boundingBox(); - - maxNodeSize = Math.max( maxNodeSize, nbb.w, nbb.h ); - } - - // 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 i = 0; i < nodeValues.length; i++ ){ - var val = nodeValues[i]; - - if( currentLevel.length > 0 ){ - var diff = Math.abs( currentLevel[0].value - val.value ); - - if( diff >= levelWidth ){ - currentLevel = []; - levels.push( currentLevel ); - } - } - - currentLevel.push( val ); - } - - // create positions from levels - - var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes - - 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 ); - - minDist = Math.min( minDist, rStep ); - } - - // find the metrics for each level - var r = 0; - for( var i = 0; i < levels.length; i++ ){ - var level = levels[i]; - 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) ); - - // 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 - - r = Math.max( rMin, r ); - } - - level.r = r; - - r += minDist; - } - - if( options.equidistant ){ - var rDeltaMax = 0; - var r = 0; - - for( var i = 0; i < levels.length; i++ ){ - var level = levels[i]; - var rDelta = level.r - r; - - rDeltaMax = Math.max( rDeltaMax, rDelta ); - } - - r = 0; - for( var i = 0; i < levels.length; i++ ){ - var level = levels[i]; - - if( i === 0 ){ - r = level.r; - } - - level.r = r; - - r += rDeltaMax; - } - } - - // calculate the node positions - var pos = {}; // id => position - for( var i = 0; i < levels.length; i++ ){ - var level = levels[i]; - var dTheta = level.dTheta; - var r = level.r; - - for( var j = 0; j < level.length; j++ ){ - var val = level[j]; - var theta = options.startAngle + (clockwise ? 1 : -1) * dTheta * j; - - var p = { - x: center.x + r * Math.cos(theta), - y: center.y + r * Math.sin(theta) - }; - - pos[ val.node.id() ] = p; - } - } - - // position the nodes - nodes.layoutPositions(this, options, function(){ - var id = this.id(); - - return pos[id]; - }); - - return this; // chaining -}; - -module.exports = ConcentricLayout; - -},{"../../math":79,"../../util":94}],48:[function(_dereq_,module,exports){ -'use strict'; - -/* -The CoSE layout was written by Gerardo Huck. -https://www.linkedin.com/in/gerardohuck/ - -Based on the following article: -http://dl.acm.org/citation.cfm?id=1498047 - -Modifications tracked on Github. -*/ - -var util = _dereq_('../../util'); -var math = _dereq_('../../math'); -var Thread = _dereq_('../../thread'); -var is = _dereq_('../../is'); - -var DEBUG; - -/** - * @brief : default layout options - */ -var defaults = { - // Called on `layoutready` - ready : function() {}, - - // Called on `layoutstop` - stop : function() {}, - - // Whether to animate while running the layout - animate : true, - - // The layout animates only after this many milliseconds - // (prevents flashing on fast runs) - animationThreshold : 250, - - // Number of iterations between consecutive screen positions update - // (0 -> only updated on the end) - refresh : 20, - - // Whether to fit the network view after when done - fit : true, - - // Padding on fit - padding : 30, - - // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h } - boundingBox : undefined, - - // Extra spacing between components in non-compound graphs - componentSpacing : 100, - - // Node repulsion (non overlapping) multiplier - nodeRepulsion : function( node ){ return 400000; }, - - // Node repulsion (overlapping) multiplier - nodeOverlap : 10, - - // Ideal edge (non nested) length - idealEdgeLength : function( edge ){ return 10; }, - - // Divisor to compute edge forces - edgeElasticity : function( edge ){ return 100; }, - - // Nesting factor (multiplier) to compute ideal edge length for nested edges - nestingFactor : 5, - - // Gravity force (constant) - gravity : 80, - - // Maximum number of iterations to perform - numIter : 1000, - - // Initial temperature (maximum node displacement) - initialTemp : 200, - - // Cooling factor (how the temperature is reduced between consecutive iterations - coolingFactor : 0.95, - - // Lower temperature threshold (below this point the layout will end) - minTemp : 1.0, - - // Whether to use threading to speed up the layout - useMultitasking : true -}; - - -/** - * @brief : constructor - * @arg options : object containing layout options - */ -function CoseLayout(options) { - this.options = util.extend({}, defaults, options); - - this.options.layout = this; -} - - -/** - * @brief : runs the layout - */ -CoseLayout.prototype.run = function() { - var options = this.options; - var cy = options.cy; - var layout = this; - var thread = this.thread; - - if( !thread || thread.stopped() ){ - thread = this.thread = Thread({ disabled: !options.useMultitasking }); - } - - layout.stopped = false; - - layout.trigger({ type: 'layoutstart', layout: layout }); - - // Set DEBUG - Global variable - if (true === options.debug) { - DEBUG = true; - } else { - DEBUG = false; - } - - // Initialize layout info - var layoutInfo = createLayoutInfo(cy, layout, options); - - // Show LayoutInfo contents if debugging - if (DEBUG) { - printLayoutInfo(layoutInfo); - } - - // If required, randomize node positions - // if (true === options.randomize) { - randomizePositions(layoutInfo, cy); - // } - - var startTime = Date.now(); - var refreshRequested = false; - var refresh = function( rOpts ){ - rOpts = rOpts || {}; - - if( refreshRequested ){ - return; - } - - if( !rOpts.force && Date.now() - startTime < options.animationThreshold ){ - return; - } - - refreshRequested = true; - - util.requestAnimationFrame(function(){ - refreshPositions(layoutInfo, cy, options); - - // Fit the graph if necessary - if (true === options.fit) { - cy.fit( options.padding ); - } - - refreshRequested = false; - }); - }; - - thread.on('message', function( e ){ - var layoutNodes = e.message; - - layoutInfo.layoutNodes = layoutNodes; - refresh(); - }); - - 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; - - /** - * @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(layoutInfo, options, step) { - // var s = "\n\n###############################"; - // s += "\nSTEP: " + step; - // s += "\n###############################\n"; - // logDebug(s); - - // 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); - }; - - /** - * @brief : Computes the node repulsion forces - */ - var calculateNodeForces = function(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; - - // s = "Set: " + graph.toString(); - // logDebug(s); - - // 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]]]; - - for (var k = j + 1; k < numNodes; k++) { - var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]]; - - nodeRepulsion(node1, node2, layoutInfo, options); - } - } - } - }; - - /** - * @brief : Compute the node repulsion forces between a pair of nodes - */ - var nodeRepulsion = function(node1, node2, layoutInfo, options) { - // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id; - - var cmptId1 = node1.cmptId; - var cmptId2 = node2.cmptId; - - if( cmptId1 !== cmptId2 && !layoutInfo.isCompound ){ return; } - - // Get direction of line connecting both node centers - var directionX = node2.positionX - node1.positionX; - var directionY = node2.positionY - node1.positionY; - // s += "\ndirectionX: " + directionX + ", directionY: " + directionY; - - // If both centers are the same, apply a random force - if (0 === directionX && 0 === directionY) { - // s += "\nNodes have the same position."; - return; // TODO could be improved with random force - } - - var overlap = nodesOverlap(node1, node2, directionX, directionY); - - 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; - - // 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 - - // Get clipping points for both nodes - var point1 = findClippingPoint(node1, directionX, directionY); - var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); - - // 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; - - // 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; - } - - // Apply force - if( !node1.isLocked ){ - node1.offsetX -= forceX; - node1.offsetY -= forceY; - } - - if( !node2.isLocked ){ - node2.offsetX += forceX; - node2.offsetY += forceY; - } - - // s += "\nForceX: " + forceX + " ForceY: " + forceY; - // logDebug(s); - - return; - }; - - /** - * @brief : Determines whether two nodes overlap or not - * @return : Amount of overlapping (0 => no overlap) - */ - var nodesOverlap = function(node1, node2, dX, dY) { - - if (dX > 0) { - var overlapX = node1.maxX - node2.minX; - } else { - var overlapX = node2.maxX - node1.minX; - } - - if (dY > 0) { - var overlapY = node1.maxY - node2.minY; - } else { - var overlapY = node2.maxY - node1.minY; - } - - if (overlapX >= 0 && overlapY >= 0) { - return Math.sqrt(overlapX * overlapX + overlapY * overlapY); - } else { - return 0; - } - }; - - /** - * @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(node, dX, dY) { - - // 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 = {}; - do { - // Case: Vertical direction (up) - if (0 === dX && 0 < dY) { - res.x = X; - // s += "\nUp direction"; - res.y = Y + H / 2; - break; - } - - // Case: Vertical direction (down) - if (0 === dX && 0 > dY) { - res.x = X; - res.y = Y + H / 2; - // s += "\nDown direction"; - break; - } - - // 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"; - break; - } - - // 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"; - break; - } - - // 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"; - break; - } - - // 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"; - break; - } - - } while (false); - - // s += "\nClipping point found at " + res.x + ", " + res.y; - // logDebug(s); - return res; - }; - - /** - * @brief : Calculates all edge forces - */ - var calculateEdgeForces = function(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]; - - // Get direction of line connecting both node centers - var directionX = target.positionX - source.positionX; - var directionY = target.positionY - source.positionY; - - // If both centers are the same, do nothing. - // A random force has already been applied as node repulsion - if (0 === directionX && 0 === directionY) { - return; - } - - // Get clipping points for both nodes - var point1 = findClippingPoint(source, directionX, directionY); - var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY); - - - var lx = point2.x - point1.x; - var ly = point2.y - point1.y; - var l = Math.sqrt(lx * lx + ly * ly); - - var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity; - - if (0 !== l) { - var forceX = force * lx / l; - var forceY = force * ly / l; - } else { - var forceX = 0; - var forceY = 0; - } - - // Add this force to target and source nodes - if( !source.isLocked ){ - source.offsetX += forceX; - source.offsetY += forceY; - } - - if( !target.isLocked ){ - target.offsetX -= forceX; - target.offsetY -= forceY; - } - - // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id; - // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")"; - // logDebug(s); - } - }; - - /** - * @brief : Computes gravity forces for all nodes - */ - var calculateGravityForces = function(layoutInfo, options) { - var distThreshold = 1; - - // var s = 'calculateGravityForces'; - // logDebug(s); - for (var i = 0; i < layoutInfo.graphSet.length; i ++) { - var graph = layoutInfo.graphSet[i]; - var numNodes = graph.length; - - // 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); - - // 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; - - if( node.isLocked ){ continue; } - - 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"; - } - // logDebug(s); - } - } - }; - - /** - * @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(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; - - // 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; - - // 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; - - // var s = "Propagating offset from parent node : " + node.id + - // ". OffsetX: " + offX + ". OffsetY: " + offY; - // s += "\n Children: " + children.toString(); - // logDebug(s); - - 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]; - } - - // Reset parent offsets - node.offsetX = 0; - node.offsetY = 0; - } - - } - }; - - /** - * @brief : Updates the layout model positions, based on - * the accumulated forces - */ - var updatePositions = function(layoutInfo, options) { - // var s = 'Updating positions'; - // logDebug(s); - - // 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; - } - } - - 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); - - // Update ancestry boudaries - updateAncestryBoundaries(n, layoutInfo); - } - - // 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); - } - } - }; - - /** - * @brief : Limits a force (forceX, forceY) to be not - * greater (in modulo) than max. - 8 Preserves force direction. - */ - var limitForce = function(forceX, forceY, max) { - // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max; - var force = Math.sqrt(forceX * forceX + forceY * forceY); - - if (force > max) { - var res = { - x : max * forceX / force, - y : max * forceY / force - }; - - } else { - var res = { - x : forceX, - y : forceY - }; - } - - // s += ".\nResult: (" + res.x + ", " + res.y + ")"; - // logDebug(s); - - return res; - }; - - /** - * @brief : Function used for keeping track of compound node - * sizes, since they should bound all their subnodes. - */ - var updateAncestryBoundaries = function(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; - } - - // Get Parent Node - var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]]; - var flag = false; - - // 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; - } - - // 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; - } - - // 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; - } - - // If updated boundaries, propagate changes upward - if (flag) { - // logDebug(s); - return updateAncestryBoundaries(p, layoutInfo); - } - - // s += ". No changes in boundaries/position of parent node " + p.id; - // logDebug(s); - return; - }; - - var separateComponents = function(layutInfo, options){ - var nodes = layoutInfo.layoutNodes; - var components = []; - - for( var i = 0; i < nodes.length; i++ ){ - var node = nodes[i]; - var cid = node.cmptId; - var component = components[ cid ] = components[ cid ] || []; - - component.push( node ); - } - - var totalA = 0; - - for( var i = 0; i < components.length; i++ ){ - var c = components[i]; - c.x1 = Infinity; - c.x2 = -Infinity; - c.y1 = Infinity; - c.y2 = -Infinity; - - for( var j = 0; j < c.length; j++ ){ - var n = c[j]; - - 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 ); - } - - c.w = c.x2 - c.x1; - c.h = c.y2 - c.y1; - - totalA += c.w * c.h; - } - - components.sort(function( c1, c2 ){ - return c2.w*c2.h - c1.w*c1.h; - }); - - var x = 0; - var y = 0; - var usedW = 0; - var rowH = 0; - var maxRowW = Math.sqrt( totalA ) * layoutInfo.clientWidth / layoutInfo.clientHeight; - - for( var i = 0; i < components.length; i++ ){ - var c = components[i]; - - for( var j = 0; j < c.length; j++ ){ - var n = c[j]; - - if( !n.isLocked ){ - n.positionX += x; - n.positionY += y; - } - } - - x += c.w + options.componentSpacing; - usedW += c.w + options.componentSpacing; - rowH = Math.max( rowH, c.h ); - - if( usedW > maxRowW ){ - y += rowH + options.componentSpacing; - x = 0; - usedW = 0; - rowH = 0; - } - } - }; - - var mainLoop = function(i){ - if( stopped ){ - // logDebug("Layout manually stopped. Stopping computation in step " + i); - return false; - } - - // Do one step in the phisical simulation - step(layoutInfo, options, i); - - // Update temperature - layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; - // logDebug("New temperature: " + layoutInfo.temperature); - - if (layoutInfo.temperature < options.minTemp) { - // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i); - return false; - } - - return true; - }; - - var i = 0; - var loopRet; - - do { - var f = 0; - - while( f < options.refresh && i < options.numIter ){ - var loopRet = mainLoop(i); - if( !loopRet ){ break; } - - f++; - i++; - } - - if( options.animate ){ - broadcast( layoutInfo.layoutNodes ); // jshint ignore:line - } - - } while ( loopRet && i + 1 < options.numIter ); - - separateComponents( layoutInfo, options ); - - return layoutInfo; - }).then(function( layoutInfoUpdated ){ - layoutInfo.layoutNodes = layoutInfoUpdated.layoutNodes; // get the positions - - thread.stop(); - done(); - }); - - var done = function(){ - refresh({ force: true }); - - // Layout has finished - layout.one('layoutstop', options.stop); - layout.trigger({ type: 'layoutstop', layout: layout }); - }; - - return this; // chaining -}; - - -/** - * @brief : called on continuous layouts to stop them before they finish - */ -CoseLayout.prototype.stop = function(){ - this.stopped = true; - - if( this.thread ){ - this.thread.stop(); - } - - this.trigger('layoutstop'); - - return this; // chaining -}; - -CoseLayout.prototype.destroy = function(){ - if( this.thread ){ - this.thread.stop(); - } - - return this; // chaining -}; - - -/** - * @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(cy, layout, options) { - // Shortcut - var edges = options.eles.edges(); - var nodes = options.eles.nodes(); - - 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() - } ) - }; - - var components = options.eles.components(); - var id2cmptId = {}; - - for( var i = 0; i < components.length; i++ ){ - var component = components[i]; - - for( var j = 0; j < component.length; j++ ){ - var node = component[j]; - - id2cmptId[ node.id() ] = i; - } - } - - // Iterate over all nodes, creating layout nodes - for (var i = 0; i < layoutInfo.nodeSize; i++) { - var n = nodes[i]; - var nbb = n.boundingBox(); - - 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-left') ); - tempNode.padRight = parseFloat( n.style('padding-right') ); - tempNode.padTop = parseFloat( n.style('padding-top') ); - tempNode.padBottom = parseFloat( n.style('padding-bottom') ); - - // forces - tempNode.nodeRepulsion = is.fn( options.nodeRepulsion ) ? options.nodeRepulsion.call( n, n ) : options.nodeRepulsion; - - // Add new node - layoutInfo.layoutNodes.push(tempNode); - // Add entry to id-index map - layoutInfo.idToIndex[tempNode.id] = i; - } - - // 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 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); - } - } - - // Add root graph to graphSet - layoutInfo.graphSet.push(tempGraph); - - // 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]; - } - } - } - - // 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; - } - } - - // 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'); - - // Compute ideal length - var idealLength = is.fn( options.idealEdgeLength ) ? options.idealEdgeLength.call( e, e ) : options.idealEdgeLength; - var elasticity = is.fn( options.edgeElasticity ) ? options.edgeElasticity.call( e, e ) : options.edgeElasticity; - - // 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 (sourceGraph != targetGraph) { - // Find lowest common graph ancestor - var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); - - // Compute sum of node depths, relative to lca graph - var lcaGraph = layoutInfo.graphSet[lca]; - var depth = 0; - - // Source depth - var tempNode = layoutInfo.layoutNodes[sourceIx]; - while ( -1 === lcaGraph.indexOf(tempNode.id) ) { - tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]]; - depth++; - } - - // Target depth - tempNode = layoutInfo.layoutNodes[targetIx]; - while ( -1 === lcaGraph.indexOf(tempNode.id) ) { - tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]]; - depth++; - } - - // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId + - // ". Index: " + lca + " Contents: " + lcaGraph.toString() + - // ". Depth: " + depth); - - // Update idealLength - idealLength *= depth * options.nestingFactor; - } - - tempEdge.idealLength = idealLength; - tempEdge.elasticity = elasticity; - - layoutInfo.layoutEdges.push(tempEdge); - } - - // Finally, return layoutInfo object - return layoutInfo; -}; - - -/** - * @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(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; - } -}; - - -/** - * @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(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(layoutInfo) { - /* jshint ignore:start */ - - 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; - /* jshint ignore:end */ -}; - - -/** - * @brief : Randomizes the position of all nodes - */ -var randomizePositions = function(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(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(i, ele) { - 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.trigger({ 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; - -},{"../../is":77,"../../math":79,"../../thread":92,"../../util":94}],49:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); -var math = _dereq_('../../math'); - -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 - 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( 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 - ready: undefined, // callback on layoutready - stop: undefined // callback on layoutstop -}; - -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(){ - 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(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(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.boundingBox(); - 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(row, col){ - return cellUsed['c-' + row + '-' + col] ? true : false; - }; - - var use = function(row, col){ - cellUsed['c-' + row + '-' + col] = true; - }; - - // to keep track of current cell position - var row = 0; - var col = 0; - var moveToNextCell = function(){ - 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(i, element){ - var x, y; - - if( element.locked() || element.isFullAutoParent() ){ - 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; - -},{"../../math":79,"../../util":94}],50:[function(_dereq_,module,exports){ -'use strict'; - -module.exports = [ - { name: 'breadthfirst', impl: _dereq_('./breadthfirst') }, - { name: 'circle', impl: _dereq_('./circle') }, - { name: 'concentric',impl: _dereq_('./concentric') }, - { name: 'cose', impl: _dereq_('./cose') }, - { name: 'grid', impl: _dereq_('./grid') }, - { name: 'null', impl: _dereq_('./null') }, - { name: 'preset', impl: _dereq_('./preset') }, - { name: 'random', impl: _dereq_('./random') } -]; - -},{"./breadthfirst":45,"./circle":46,"./concentric":47,"./cose":48,"./grid":49,"./null":51,"./preset":52,"./random":53}],51:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); - -// default layout options -var defaults = { - ready: function(){}, // on layoutready - stop: function(){} // 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.trigger('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.trigger('layoutready'); - - // trigger layoutstop when the layout stops (e.g. finishes) - layout.one('layoutstop', options.stop); - layout.trigger('layoutstop'); - - return this; // chaining -}; - -// called on continuous layouts to stop them before they finish -NullLayout.prototype.stop = function(){ - return this; // chaining -}; - -module.exports = NullLayout; - -},{"../../util":94}],52:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); -var is = _dereq_('../../is'); - -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 - ready: undefined, // callback on layoutready - stop: undefined // callback on layoutstop -}; - -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.apply( node, [ node ] ); - } - - var pos = options.positions[node._private.data.id]; - - if( pos == null ){ - return null; - } - - return pos; - } - - nodes.layoutPositions(this, options, function(i, node){ - var position = getPosition(node); - - if( node.locked() || position == null ){ - return false; - } - - return position; - }); - - return this; // chaining -}; - -module.exports = PresetLayout; - -},{"../../is":77,"../../util":94}],53:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../../util'); -var math = _dereq_('../../math'); - -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 - ready: undefined, // callback on layoutready - stop: undefined // callback on layoutstop -}; - -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( i, node ){ - 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; - -},{"../../math":79,"../../util":94}],54:[function(_dereq_,module,exports){ -'use strict'; - -var math = _dereq_('../../../math'); -var is = _dereq_('../../../is'); -var util = _dereq_('../../../util'); - -var BRp = {}; - -BRp.arrowShapeHeight = 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( x, y, size, angle, translation, 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); - - return inside; - }; - - var transform = function( 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; - - return { - x: xTranslated, - y: yTranslated - }; - }; - - var transformPoints = function( pts, size, angle, translation ){ - var retPts = []; - - for( var i = 0; i < pts.length; i += 2 ){ - var x = pts[i]; - var y = pts[i + 1]; - - retPts.push( transform(x, y, size, angle, translation) ); - } - - return retPts; - }; - - var pointsToArr = function( pts ){ - var ret = []; - - for( var i = 0; i < pts.length; i++ ){ - var p = pts[i]; - - ret.push( p.x, p.y ); - } - - return ret; - }; - - var defineArrowShape = function( name, defn ){ - if( is.string(defn) ){ - defn = arrowShapes[ defn ]; - } - - arrowShapes[ name ] = util.extend( { - name: name, - - points: [ - -0.15, -0.3, - 0.15, -0.3, - 0.15, 0.3, - -0.15, 0.3 - ], - - collide: function( x, y, size, angle, translation, padding ){ - var points = pointsToArr( transformPoints( this.points, size + 2*padding, angle, translation ) ); - var inside = math.pointInsidePolygonPoints( x, y, points ); - - return inside; - }, - - roughCollide: bbCollide, - - draw: function( context, size, angle, translation ){ - var points = transformPoints( this.points, size, angle, translation ); - - renderer.arrowShapeImpl('polygon')( context, points ); - }, - - spacing: function( edge ){ - return 0; - }, - - gap: function( edge ){ - return edge._private.style['width'].pfValue * 2; - } - }, defn ); - }; - - defineArrowShape( 'none', { - collide: util.falsify, - - roughCollide: util.falsify, - - draw: util.noop, - - spacing: util.zeroify, - - gap: util.zeroify - } ); - - defineArrowShape( 'triangle', { - points: [ - -0.15, -0.3, - 0, 0, - 0.15, -0.3 - ] - } ); - - defineArrowShape( 'arrow', 'triangle' ); - - defineArrowShape( 'triangle-backcurve', { - points: arrowShapes['triangle'].points, - - controlPoint: [ 0, -0.15 ], - - roughCollide: bbCollide, - - draw: function( context, size, angle, translation ){ - var ptsTrans = transformPoints( this.points, size, angle, translation ); - var ctrlPt = this.controlPoint; - var ctrlPtTrans = transform( ctrlPt[0], ctrlPt[1], size, angle, translation ); - - renderer.arrowShapeImpl( this.name )( context, ptsTrans, ctrlPtTrans ); - }, - - gap: function( edge ){ - return edge._private.style['width'].pfValue; - } - } ); - - - 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 - ], - - collide: function( x, y, size, angle, translation, padding ){ - var triPts = pointsToArr( transformPoints( this.points, size + 2*padding, angle, translation ) ); - var teePts = pointsToArr( transformPoints( this.pointsTee, size + 2*padding, angle, translation ) ); - - var inside = math.pointInsidePolygonPoints( x, y, triPts ) || math.pointInsidePolygonPoints( x, y, teePts ); - - return inside; - }, - - draw: function( context, size, angle, translation ){ - var triPts = transformPoints( this.points, size, angle, translation ); - var teePts = transformPoints( this.pointsTee, size, angle, translation ); - - renderer.arrowShapeImpl( this.name )( context, triPts, teePts ); - } - } ); - - defineArrowShape( 'vee', { - points: [ - -0.15, -0.3, - 0, 0, - 0.15, -0.3, - 0, -0.15, - ], - - gap: function( edge ){ - return edge._private.style['width'].pfValue; - } - } ); - - defineArrowShape( 'half-triangle-overshot', { - points: [ - 0, -0.25, - -0.5, -0.25, - 0.5, 0.25 - ], - - leavePathOpen: true, - - matchEdgeWidth: true - } ); - - defineArrowShape( 'circle', { - radius: 0.15, - - collide: function( x, y, size, angle, translation, 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) ); - - return inside; - }, - - draw: function( context, size, angle, translation ){ - renderer.arrowShapeImpl( this.name )( context, translation.x, translation.y, this.radius * size ); - }, - - spacing: function( edge ){ - return renderer.getArrowWidth(edge._private.style['width'].pfValue) - * this.radius; - }, - } ); - - defineArrowShape( 'inhibitor', { - points: [ - -0.25, 0, - -0.25, -0.1, - 0.25, -0.1, - 0.25, 0 - ], - - spacing: function( edge ){ - return 1; - }, - - gap: function( edge ){ - return 1; - } - } ); - - defineArrowShape( 'tee', 'inhibitor' ); - - defineArrowShape( 'square', { - points: [ - -0.15, 0.00, - 0.15, 0.00, - 0.15, -0.3, - -0.15, -0.3 - ] - } ); - - defineArrowShape( 'diamond', { - points: [ - -0.15, -0.15, - 0, -0.3, - 0.15, -0.15, - 0, 0 - ], - - gap: function( edge ){ - return edge._private.style['width'].pfValue; - } - } ); - -}; - -module.exports = BRp; - -},{"../../../is":77,"../../../math":79,"../../../util":94}],55:[function(_dereq_,module,exports){ -'use strict'; - -var BRp = {}; - -var delEleCache = function( r ){ - r.eleEache = null; -}; - -var getEleCache = function( r ){ - if( !r.eleEache ){ - r.eleEache = { - nodes: r.cy.nodes(), - edges: r.cy.edges() - }; - } - - return r.eleEache; -}; - -BRp.getCachedElements = function(){ - return getEleCache( this ); -}; - -BRp.getCachedNodes = function(){ - return getEleCache( this ).nodes; -}; - -BRp.getCachedEdges = function(){ - return getEleCache( this ).edges; -}; - -BRp.updateElementsCache = function(){ - var r = this; - - delEleCache( r ); - - return getEleCache( r ); -}; - -module.exports = BRp; - -},{}],56:[function(_dereq_,module,exports){ -'use strict'; - -var math = _dereq_('../../../math'); -var is = _dereq_('../../../is'); -var zIndexSort = _dereq_('../../../collection/zsort'); - -var BRp = {}; - -// Project mouse -BRp.projectIntoViewport = function(clientX, clientY) { - var offsets = this.findContainerClientCoords(); - var offsetLeft = offsets[0]; - var offsetTop = offsets[1]; - - var x = clientX - offsetLeft; - var y = clientY - offsetTop; - - x -= this.cy.pan().x; y -= this.cy.pan().y; x /= this.cy.zoom(); y /= this.cy.zoom(); - return [x, y]; -}; - -BRp.findContainerClientCoords = function() { - var container = this.container; - - var bb = this.containerBB = this.containerBB || container.getBoundingClientRect(); - - return [bb.left, bb.top, bb.right - bb.left, bb.bottom - bb.top]; -}; - -BRp.invalidateContainerClientCoordsCache = function(){ - this.containerBB = null; -}; - -// Find nearest element -BRp.findNearestElement = function(x, y, visibleElementsOnly, isTouch){ - var self = this; - var r = this; - var eles = r.getCachedZSortedEles(); - var near = []; - 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; - - function checkNode(node){ - var _p = node._private; - - if( _p.style['events'].strValue === 'no' ){ return; } - - var width = node.outerWidth() + 2*nodeThreshold; - var height = node.outerHeight() + 2*nodeThreshold; - var hw = width/2; - var hh = height/2; - var pos = _p.position; - - if( - pos.x - hw <= x && x <= pos.x + hw // bb check x - && - pos.y - hh <= y && y <= pos.y + hh // bb check y - ){ - var visible = !visibleElementsOnly || ( node.visible() && !node.transparent() ); - - // exit early if invisible edge and must be visible - if( visibleElementsOnly && !visible ){ - return; - } - - var shape = r.nodeShapes[ self.getNodeShape(node) ]; - - if( - shape.checkPoint(x, y, 0, width, height, pos.x, pos.y) - ){ - near.push( node ); - } - - } - } - - function checkEdge(edge){ - var _p = edge._private; - - if( _p.style['events'].strValue === 'no' ){ return; } - - var rs = _p.rscratch; - var style = _p.style; - var width = style['width'].pfValue/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; - - // exit early if invisible edge and must be visible - var passedVisibilityCheck; - var passesVisibilityCheck = function(){ - if( passedVisibilityCheck !== undefined ){ - return passedVisibilityCheck; - } - - if( !visibleElementsOnly ){ - passedVisibilityCheck = true; - return true; - } - - var visible = edge.visible() && !edge.transparent(); - if( visible ){ - passedVisibilityCheck = true; - return true; - } - - passedVisibilityCheck = false; - return false; - }; - - 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)) - && passesVisibilityCheck() && - widthSq > ( sqDist = math.sqDistanceToFiniteLine(x, y, pts[i], pts[i+1], pts[i+2], pts[i+3]) ) - ){ - near.push( edge ); - } - } - - } 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)) - && passesVisibilityCheck() && - (widthSq > (sqDist = math.sqDistanceToQuadraticBezier(x, y, pts[i], pts[i+1], pts[i+2], pts[i+3], pts[i+4], pts[i+5])) ) - ){ - near.push( edge ); - } - } - } - - // if we're close to the edge but didn't hit it, maybe we hit its arrows - if( inEdgeBB && passesVisibilityCheck() && near.length === 0 || near[near.length - 1] !== edge ){ - var src = src || _p.source; - var tgt = tgt || _p.target; - - var eWidth = style['width'].pfValue; - var arSize = self.getArrowWidth( eWidth ); - - 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 } - ]; - - for( var i = 0; i < arrows.length; i++ ){ - var ar = arrows[i]; - var shape = r.arrowShapes[ style[ar.name+'-arrow-shape'].value ]; - - if( - shape.roughCollide(x, y, arSize, ar.angle, { x: ar.x, y: ar.y }, edgeThreshold) - && - shape.collide(x, y, arSize, ar.angle, { x: ar.x, y: ar.y }, edgeThreshold) - ){ - near.push( edge ); - break; - } - } - } - - // 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 && near[ near.length - 1 ] === edge ){ - checkNode( src ); - checkNode( tgt ); - } - } - - function checkLabel(ele){ - var _p = ele._private; - var th = labelThreshold; - - if( _p.style['text-events'].strValue === 'no' ){ return; } - - // adjust bb w/ angle - if( _p.group === 'edges' && _p.style['edge-text-rotation'].strValue === 'autorotate' ){ - - var rstyle = _p.rstyle; - var lw = rstyle.labelWidth + 2*th; - var lh = rstyle.labelHeight + 2*th; - var lx = rstyle.labelX; - var ly = rstyle.labelY; - - var theta = _p.rscratch.labelAngle; - var cos = Math.cos( theta ); - var sin = Math.sin( theta ); - - var rotate = function( x, y ){ - x = x - lx; - y = y - ly; - - return { - x: x*cos - y*sin + lx, - y: x*sin + y*cos + ly - }; - }; - - var lx1 = lx - lw/2; - var lx2 = lx + lw/2; - var ly1 = ly - lh/2; - var ly2 = ly + lh/2; - - 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 ) ){ - near.push( ele ); - } - - } else { - var bb = ele.boundingBox({ - includeLabels: true, - includeNodes: false, - includeEdges: false - }); - - // adjust bb w/ threshold - bb.x1 -= th; - bb.y1 -= th; - bb.x2 += th; - bb.y2 += th; - bb.w = bb.x2 - bb.x1; - bb.h = bb.y2 - bb.y1; - - if( math.inBoundingBox( bb, x, y ) ){ - near.push( ele ); - } - } - - } - - for( var i = eles.length - 1; i >= 0; i-- ){ // reverse order for precedence - var ele = eles[i]; - var _p = ele._private; - - if( near.length > 0 ){ break; } // since we check in z-order, first found is top and best result => exit early - - if( _p.group === 'nodes' ){ - checkNode( ele ); - - } else { // then edge - checkEdge( ele ); - } - - checkLabel( ele ); - - } - - - if( near.length > 0 ){ - return near[ near.length - 1 ]; - } else { - return null; - } -}; - -// 'Give me everything from this box' -BRp.getAllInBox = function(x1, y1, x2, y2) { - var nodes = this.getCachedNodes(); - var edges = this.getCachedEdges(); - var box = []; - - var x1c = Math.min(x1, x2); - var x2c = Math.max(x1, x2); - var y1c = Math.min(y1, y2); - var y2c = Math.max(y1, y2); - - x1 = x1c; - x2 = x2c; - y1 = y1c; - y2 = y2c; - - var boxBb = math.makeBoundingBox({ - x1: x1, y1: y1, - x2: x2, y2: y2 - }); - - for ( var i = 0; i < nodes.length; i++ ){ - var node = nodes[i]; - var nodeBb = node.boundingBox({ - includeNodes: true, - includeEdges: false, - includeLabels: false - }); - - if( math.boundingBoxesIntersect(boxBb, nodeBb) ){ - box.push(nodes[i]); - } - } - - for( var e = 0; e < edges.length; e++ ){ - var edge = edges[e]; - var _p = edge._private; - var rs = _p.rscratch; - - 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; } - - if( rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack' ){ - - var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts; - var allInside = true; - - for( var i = 0; i < pts.length; i++ ){ - if( !math.pointInBoundingBox( boxBb, pts[i] ) ){ - allInside = false; - break; - } - } - - if( allInside ){ - box.push( edge ); - } - - } else if( rs.edgeType === 'haystack' || rs.edgeType === 'straight' ){ - box.push( edge ); - } - - } - - return box; -}; - - -/** - * Returns the shape of the given node. If the height or width of the given node - * is set to auto, the node is considered to be a compound. - * - * @param node a node - * @return {String} shape of the node - */ -BRp.getNodeShape = function( node ){ - var r = this; - var style = node._private.style; - var shape = style['shape'].value; - - if( node.isParent() ){ - if( shape === 'rectangle' || shape === 'roundrectangle' ){ - return shape; - } else { - return 'rectangle'; - } - } - - if( shape === 'polygon' ){ - var points = style['shape-polygon-points'].value; - - return r.nodeShapes.makePolygon( points ).name; - } - - return shape; -}; - -BRp.updateCachedZSortedEles = function(){ - this.getCachedZSortedEles( true ); -}; - -BRp.getCachedZSortedEles = function( forceRecalc ){ - var lastNodes = this.lastZOrderCachedNodes; - var lastEdges = this.lastZOrderCachedEdges; - var nodes = this.getCachedNodes(); - var edges = this.getCachedEdges(); - var eles = []; - - if( forceRecalc || !lastNodes || !lastEdges || lastNodes !== nodes || lastEdges !== edges ){ - //console.time('cachezorder') - - for( var i = 0; i < nodes.length; i++ ){ - var n = nodes[i]; - - if( n.animated() || (n.visible() && !n.transparent()) ){ - eles.push( n ); - } - } - - for( var i = 0; i < edges.length; i++ ){ - var e = edges[i]; - - if( e.animated() || (e.visible() && !e.transparent()) ){ - eles.push( e ); - } - } - - eles.sort( zIndexSort ); - this.cachedZSortedEles = eles; - //console.log('make cache') - - //console.timeEnd('cachezorder') - } else { - eles = this.cachedZSortedEles; - //console.log('read cache') - } - - this.lastZOrderCachedNodes = nodes; - this.lastZOrderCachedEdges = edges; - - return eles; -}; - -function pushBezierPts(edge, pts){ - var qbezierAt = function( p1, p2, p3, t ){ return math.qbezierAt(p1, p2, p3, t); }; - var _p = edge._private; - var bpts = _p.rstyle.bezierPts; - - bpts.push({ - x: qbezierAt( pts[0], pts[2], pts[4], 0.05 ), - y: qbezierAt( pts[1], pts[3], pts[5], 0.05 ) - }); - - bpts.push({ - x: qbezierAt( pts[0], pts[2], pts[4], 0.25 ), - y: qbezierAt( pts[1], pts[3], pts[5], 0.25 ) - }); - - bpts.push({ - x: qbezierAt( pts[0], pts[2], pts[4], 0.4 ), - y: qbezierAt( pts[1], pts[3], pts[5], 0.4 ) - }); - - bpts.push({ - x: qbezierAt( pts[0], pts[2], pts[4], 0.5 ), - y: qbezierAt( pts[1], pts[3], pts[5], 0.5 ) - }); - - bpts.push({ - x: qbezierAt( pts[0], pts[2], pts[4], 0.6 ), - y: qbezierAt( pts[1], pts[3], pts[5], 0.6 ) - }); - - bpts.push({ - x: qbezierAt( pts[0], pts[2], pts[4], 0.75 ), - y: qbezierAt( pts[1], pts[3], pts[5], 0.75 ) - }); - - bpts.push({ - x: qbezierAt( pts[0], pts[2], pts[4], 0.95 ), - y: qbezierAt( pts[1], pts[3], pts[5], 0.95 ) - }); -} - -BRp.projectLines = function( edge ){ - var _p = edge._private; - var rs = _p.rscratch; - var et = rs.edgeType; - - if( et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound' ){ - var bpts = _p.rstyle.bezierPts = []; // jshint ignore:line - - for( var i = 0; i + 5 < rs.allpts.length; i += 4 ){ - pushBezierPts( edge, rs.allpts.slice(i, i+6) ); - } - } else if( et === 'segments' ){ - var lpts = _p.rstyle.linePts = []; - - 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; - - _p.rstyle.haystackPts = [ - { x: hpts[0], y: hpts[1] }, - { x: hpts[2], y: hpts[3] } - ]; - } -}; - -BRp.projectBezier = BRp.projectLines; - -BRp.recalculateNodeLabelProjection = function( node ){ - var content = node._private.style['label'].strValue; - if( !content || content.match(/^\s+$/) ){ return; } - - var textX, textY; - var nodeWidth = node.outerWidth(); - var nodeHeight = node.outerHeight(); - var nodePos = node._private.position; - var textHalign = node._private.style['text-halign'].strValue; - var textValign = node._private.style['text-valign'].strValue; - var rs = node._private.rscratch; - var rstyle = node._private.rstyle; - - switch( textHalign ){ - case 'left': - textX = nodePos.x - nodeWidth / 2; - break; - - case 'right': - textX = nodePos.x + nodeWidth / 2; - break; - - default: // e.g. center - textX = nodePos.x; - } - - switch( textValign ){ - case 'top': - textY = nodePos.y - nodeHeight / 2; - break; - - case 'bottom': - textY = nodePos.y + nodeHeight / 2; - break; - - default: // e.g. middle - textY = nodePos.y; - } - - rs.labelX = textX; - rs.labelY = textY; - rstyle.labelX = textX; - rstyle.labelY = textY; - - this.applyLabelDimensions( node ); -}; - -BRp.recalculateEdgeLabelProjection = function( edge ){ - var content = edge._private.style['label'].strValue; - if( !content || content.match(/^\s+$/) ){ return; } - - var textX, textY; - var _p = edge._private; - var rs = _p.rscratch; - //var style = _p.style; - var rstyle = _p.rstyle; - - textX = rs.midX; - textY = rs.midY; - - // add center point to style so bounding box calculations can use it - rs.labelX = textX; - rs.labelY = textY; - rstyle.labelX = textX; - rstyle.labelY = textY; - - this.applyLabelDimensions( edge ); -}; - -BRp.applyLabelDimensions = function( ele ){ - var rs = ele._private.rscratch; - var rstyle = ele._private.rstyle; - - var text = this.getLabelText( ele ); - var labelDims = this.calculateLabelDimensions( ele, text ); - - rstyle.labelWidth = labelDims.width; - rs.labelWidth = labelDims.width; - - rstyle.labelHeight = labelDims.height; - rs.labelHeight = labelDims.height; -}; - -BRp.getLabelText = function( ele ){ - var style = ele._private.style; - var text = ele._private.style['label'].strValue; - var textTransform = style['text-transform'].value; - var rscratch = ele._private.rscratch; - - if (textTransform == 'none') { - } else if (textTransform == 'uppercase') { - text = text.toUpperCase(); - } else if (textTransform == 'lowercase') { - text = text.toLowerCase(); - } - - if( style['text-wrap'].value === 'wrap' ){ - //console.log('wrap'); - - // save recalc if the label is the same as before - if( rscratch.labelWrapKey === rscratch.labelKey ){ - // console.log('wrap cache hit'); - return rscratch.labelWrapCachedText; - } - // console.log('wrap cache miss'); - - var lines = text.split('\n'); - var maxW = style['text-max-width'].pfValue; - var wrappedLines = []; - - for( var l = 0; l < lines.length; l++ ){ - var line = lines[l]; - var lineDims = this.calculateLabelDimensions( ele, line, 'line=' + line ); - var lineW = lineDims.width; - - if( lineW > maxW ){ // line is too long - var words = line.split(/\s+/); // NB: assume collapsed whitespace into single space - var subline = ''; - - 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; - - if( testW <= maxW ){ // word fits on current line - subline += word + ' '; - } else { // word starts new line - wrappedLines.push( subline ); - subline = word + ' '; - } - } - - // 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 - - rscratch.labelWrapCachedLines = wrappedLines; - rscratch.labelWrapCachedText = text = wrappedLines.join('\n'); - rscratch.labelWrapKey = rscratch.labelKey; - - // console.log(text) - } // if wrap - - return text; -}; - -BRp.calculateLabelDimensions = function( ele, text, extraKey ){ - var r = this; - var style = ele._private.style; - var fStyle = style['font-style'].strValue; - var size = style['font-size'].pfValue + 'px'; - var family = style['font-family'].strValue; - // var variant = style['font-variant'].strValue; - var weight = style['font-weight'].strValue; - - var cacheKey = ele._private.labelKey; - - if( extraKey ){ - cacheKey += '$@$' + extraKey; - } - - var cache = r.labelDimCache || (r.labelDimCache = {}); - - if( cache[cacheKey] ){ - return cache[cacheKey]; - } - - var div = this.labelCalcDiv; - - if( !div ){ - div = this.labelCalcDiv = document.createElement('div'); - document.body.appendChild( div ); - } - - var ds = div.style; - - // from ele style - ds.fontFamily = family; - ds.fontStyle = fStyle; - ds.fontSize = size; - // ds.fontVariant = variant; - 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( style['text-wrap'].value === 'wrap' ){ - ds.whiteSpace = 'pre'; // so newlines are taken into account - } else { - ds.whiteSpace = 'normal'; - } - - // put label content in div - div.textContent = text; - - cache[cacheKey] = { - width: div.clientWidth, - height: div.clientHeight - }; - - return cache[cacheKey]; -}; - -BRp.recalculateRenderedStyle = function( eles ){ - var edges = []; - var nodes = []; - var handledEdge = {}; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - var _p = ele._private; - var style = _p.style; - var rs = _p.rscratch; - var rstyle = _p.rstyle; - var id = _p.data.id; - var bbStyleSame = rs.boundingBoxKey != null && _p.boundingBoxKey === rs.boundingBoxKey; - var labelStyleSame = rs.labelKey != null && _p.labelKey === rs.labelKey; - var styleSame = bbStyleSame && labelStyleSame; - - if( _p.group === 'nodes' ){ - var pos = _p.position; - var posSame = rstyle.nodeX != null && rstyle.nodeY != null && pos.x === rstyle.nodeX && pos.y === rstyle.nodeY; - var wSame = rstyle.nodeW != null && rstyle.nodeW === style['width'].pfValue; - var hSame = rstyle.nodeH != null && rstyle.nodeH === style['height'].pfValue; - - if( !posSame || !styleSame || !wSame || !hSame ){ - nodes.push( ele ); - } - - rstyle.nodeX = pos.x; - rstyle.nodeY = pos.y; - rstyle.nodeW = style['width'].pfValue; - rstyle.nodeH = style['height'].pfValue; - } else { // edges - - var srcPos = _p.source._private.position; - var tgtPos = _p.target._private.position; - var srcSame = rstyle.srcX != null && rstyle.srcY != null && srcPos.x === rstyle.srcX && srcPos.y === rstyle.srcY; - var tgtSame = rstyle.tgtX != null && rstyle.tgtY != null && tgtPos.x === rstyle.tgtX && tgtPos.y === rstyle.tgtY; - var positionsSame = srcSame && tgtSame; - - if( !positionsSame || !styleSame ){ - if( rs.edgeType === 'bezier' || rs.edgeType === 'straight' || rs.edgeType === 'self' || rs.edgeType === 'compound' ){ - if( !handledEdge[ id ] ){ - edges.push( ele ); - handledEdge[ id ] = true; - - var parallelEdges = ele.parallelEdges(); - for( var i = 0; i < parallelEdges.length; i++ ){ - var pEdge = parallelEdges[i]; - var pId = pEdge._private.data.id; - - if( !handledEdge[ pId ] ){ - edges.push( pEdge ); - handledEdge[ pId ] = true; - } - - } - } - } else { - edges.push( ele ); - } - } // if positions diff - - // update rstyle positions - rstyle.srcX = srcPos.x; - rstyle.srcY = srcPos.y; - rstyle.tgtX = tgtPos.x; - rstyle.tgtY = tgtPos.y; - - } // if edges - - rs.boundingBoxKey = _p.boundingBoxKey; - rs.labelKey = _p.labelKey; - } - - this.recalculateEdgeProjections( edges ); - this.recalculateLabelProjections( nodes, edges ); -}; - -BRp.recalculateLabelProjections = function( nodes, edges ){ - for( var i = 0; i < nodes.length; i++ ){ - this.recalculateNodeLabelProjection( nodes[i] ); - } - - for( var i = 0; i < edges.length; i++ ){ - this.recalculateEdgeLabelProjection( edges[i] ); - } -}; - -BRp.recalculateEdgeProjections = function( edges ){ - this.findEdgeControlPoints( edges ); -}; - - -// Find edge control points -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 = []; - var autorotateEdges = []; - - // 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 style = _p.style; - var curveStyle = style['curve-style'].value; - var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments'; - - // ignore edges who are not to be displayed - // they shouldn't take up space - if( style.display.value === 'none' ){ - continue; - } - - if( style['edge-text-rotation'].strValue === 'autorotate' ){ - autorotateEdges.push( edge ); - } - - if( curveStyle === 'haystack' ){ - haystackEdges.push( edge ); - continue; - } - - var srcId = data.source; - var tgtId = data.target; - - pairId = srcId > tgtId ? - tgtId + '$-$' + srcId : - srcId + '$-$' + tgtId ; - - if( edgeIsUnbundled ){ - pairId = 'unbundled' + '$-$' + data.id; - } - - if( hashTable[pairId] == null ){ - hashTable[pairId] = []; - pairIds.push( pairId ); - } - - hashTable[pairId].push( edge ); - - if( edgeIsUnbundled ){ - hashTable[pairId].hasUnbundled = true; - } - } - - var src, tgt, src_p, tgt_p, srcPos, tgtPos, srcW, srcH, tgtW, tgtH, srcShape, tgtShape; - var vectorNormInverse; - var badBezier; - - // 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]; - - // for each pair id, the edges should be sorted by index - pairEdges.sort(function(edge1, edge2){ - return edge1._private.index - edge2._private.index; - }); - - src = pairEdges[0]._private.source; - tgt = pairEdges[0]._private.target; - - src_p = src._private; - tgt_p = tgt._private; - - // make sure src/tgt distinction is consistent - // (src/tgt in this case are just for ctrlpts and don't actually have to be true src/tgt) - if( src_p.data.id > tgt_p.data.id ){ - var temp = src; - src = tgt; - tgt = temp; - } - - srcPos = src_p.position; - tgtPos = tgt_p.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) ]; - - badBezier = false; - - - if( (pairEdges.length > 1 && src !== tgt) || pairEdges.hasUnbundled ){ - - // 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 - ); - - // 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 midptSrcPts = { - x1: srcOutside[0], - x2: tgtOutside[0], - y1: srcOutside[1], - y2: tgtOutside[1] - }; - - var dy = ( tgtOutside[1] - srcOutside[1] ); - var dx = ( tgtOutside[0] - srcOutside[0] ); - var l = Math.sqrt( dx*dx + dy*dy ); - - var vector = { - x: dx, - y: dy - }; - - var vectorNorm = { - x: vector.x/l, - y: vector.y/l - }; - vectorNormInverse = { - x: -vectorNorm.y, - y: vectorNorm.x - }; - - - // if src intersection is inside tgt or tgt intersection is inside src, 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 edge; - var edge_p; - var rs; - - for (var i = 0; i < pairEdges.length; i++) { - edge = pairEdges[i]; - edge_p = edge._private; - rs = edge_p.rscratch; - - var edgeIndex1 = rs.lastEdgeIndex; - var edgeIndex2 = i; - - var numEdges1 = rs.lastNumEdges; - var numEdges2 = pairEdges.length; - - var eStyle = edge_p.style; - var style = eStyle; - var curveStyle = eStyle['curve-style'].value; - var ctrlptDists = eStyle['control-point-distances']; - var ctrlptWs = eStyle['control-point-weights']; - var bezierN = ctrlptDists && ctrlptWs ? Math.min( ctrlptDists.value.length, ctrlptWs.value.length ) : 1; - var stepSize = eStyle['control-point-step-size'].pfValue; - var ctrlptDist = ctrlptDists !== undefined ? ctrlptDists.pfValue[0] : undefined; - var ctrlptWeight = ctrlptWs.value[0]; - var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments'; - - var swappedDirection = edge_p.source !== src; - - if( swappedDirection && edgeIsUnbundled ){ - ctrlptDist *= -1; - } - - var srcX1 = rs.lastSrcCtlPtX; - var srcX2 = srcPos.x; - var srcY1 = rs.lastSrcCtlPtY; - var srcY2 = srcPos.y; - var srcW1 = rs.lastSrcCtlPtW; - var srcW2 = src.outerWidth(); - var srcH1 = rs.lastSrcCtlPtH; - var srcH2 = src.outerHeight(); - - var tgtX1 = rs.lastTgtCtlPtX; - var tgtX2 = tgtPos.x; - var tgtY1 = rs.lastTgtCtlPtY; - var tgtY2 = tgtPos.y; - var tgtW1 = rs.lastTgtCtlPtW; - var tgtW2 = tgt.outerWidth(); - var tgtH1 = rs.lastTgtCtlPtH; - var tgtH2 = tgt.outerHeight(); - - var width1 = rs.lastW; - var width2 = eStyle['control-point-step-size'].pfValue; - - if( badBezier ){ - rs.badBezier = true; - } else { - rs.badBezier = false; - } - - if( srcX1 === srcX2 && srcY1 === srcY2 && srcW1 === srcW2 && srcH1 === srcH2 - && tgtX1 === tgtX2 && tgtY1 === tgtY2 && tgtW1 === tgtW2 && tgtH1 === tgtH2 - && width1 === width2 - && ((edgeIndex1 === edgeIndex2 && numEdges1 === numEdges2) || edgeIsUnbundled) ){ - // console.log('edge ctrl pt cache HIT') - continue; // then the control points haven't changed and we can skip calculating them - } else { - 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.lastWidth = width2; - // console.log('edge ctrl pt cache MISS') - } - - if( src === tgt ){ - // Self-edge - - rs.edgeType = 'self'; - - var j = i; - var loopDist = stepSize; - - if( edgeIsUnbundled ){ - j = 0; - loopDist = ctrlptDist; - } - - rs.ctrlpts = [ - srcPos.x, - srcPos.y - (1 + Math.pow(srcH, 1.12) / 100) * loopDist * (j / 3 + 1), - - srcPos.x - (1 + Math.pow(srcW, 1.12) / 100) * loopDist * (j / 3 + 1), - srcPos.y - ]; - - } else if( - hasCompounds && - ( src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild() ) && - ( src.parents().anySame(tgt) || tgt.parents().anySame(src) ) - ){ - // Compound edge - - rs.edgeType = 'compound'; - - // 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; - - var j = i; - var loopDist = stepSize; - - if( edgeIsUnbundled ){ - j = 0; - loopDist = ctrlptDist; - } - - var loopW = 50; - - var loopaPos = { - x: srcPos.x - srcW/2, - y: srcPos.y - srcH/2 - }; - - var loopbPos = { - x: tgtPos.x - tgtW/2, - y: tgtPos.y - tgtH/2 - }; - - var loopPos = { - x: Math.min( loopaPos.x, loopbPos.x ), - y: Math.min( loopaPos.y, loopbPos.y ) - }; - - // 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) ); - - 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) - - rs.edgeType = 'segments'; - rs.segpts = []; - - var segmentWs = eStyle['segment-weights'].pfValue; - var segmentDs = eStyle['segment-distances'].pfValue; - var segmentsN = Math.min( segmentWs.length, segmentDs.length ); - - for( var s = 0; s < segmentsN; s++ ){ - var w = segmentWs[s]; - var d = segmentDs[s]; - - // d = swappedDirection ? -d : d; - // - // d = Math.abs(d); - - // var w1 = !swappedDirection ? (1 - w) : w; - // var w2 = !swappedDirection ? w : (1 - w); - - var w1 = (1 - w); - var w2 = w; - - var adjustedMidpt = { - x: midptSrcPts.x1 * w1 + midptSrcPts.x2 * w2, - y: midptSrcPts.y1 * w1 + midptSrcPts.y2 * w2 - }; - - rs.segpts.push( - adjustedMidpt.x + vectorNormInverse.x * d, - adjustedMidpt.y + vectorNormInverse.y * d - ); - } - - // Straight edge - } else if ( - pairEdges.length % 2 === 1 - && i === Math.floor(pairEdges.length / 2) - && !edgeIsUnbundled - ){ - - rs.edgeType = 'straight'; - - } else { - // (Multi)bezier - - var multi = edgeIsUnbundled; - - rs.edgeType = multi ? 'multibezier' : 'bezier'; - rs.ctrlpts = []; - - for( var b = 0; b < bezierN; b++ ){ - var normctrlptDist = (0.5 - pairEdges.length / 2 + i) * stepSize; - var manctrlptDist; - var sign = math.signum( normctrlptDist ); - - if( multi ){ - ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size - ctrlptWeight = ctrlptWs.value[b]; - } - - if( edgeIsUnbundled ){ // multi or single unbundled - manctrlptDist = ctrlptDist; - } else { - manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined; - } - - var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist; - - var w1 = !swappedDirection || edgeIsUnbundled ? (1 - ctrlptWeight) : ctrlptWeight; - var w2 = !swappedDirection || edgeIsUnbundled ? ctrlptWeight : (1 - ctrlptWeight); - - var adjustedMidpt = { - x: midptSrcPts.x1 * w1 + midptSrcPts.x2 * w2, - y: midptSrcPts.y1 * w1 + midptSrcPts.y2 * w2 - }; - - rs.ctrlpts.push( - adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, - adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint - ); - } - - } - - // find endpts for edge - this.findEndpoints( edge ); - - 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 ); - - var minCpADistFactor = 3; - var arrowW = this.getArrowWidth( eStyle['width'].pfValue ) * this.arrowShapeHeight; - var minCpADist = minCpADistFactor * arrowW; - - if( rs.edgeType === 'bezier' ){ - var startACpDist = math.distance( { x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.startX, y: rs.startY } ); - var closeStartACp = startACpDist < minCpADist; - var endACpDist = math.distance( { x: rs.ctrlpts[0], y: rs.ctrlpts[1] }, { x: rs.endX, y: rs.endY } ); - var closeEndACp = endACpDist < minCpADist; - - var overlapping = false; - - if( badStart || badAStart || closeStartACp ){ - overlapping = true; - - // 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 - ); - - 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; - } - } - - if( badEnd || badAEnd || closeEndACp ){ - overlapping = true; - - // 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 - ); - - 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; - } - - } - - if( overlapping ){ - // recalc endpts - this.findEndpoints( edge ); - } - - } - - if( rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' ){ - rs.allpts = []; - - rs.allpts.push( rs.startX, rs.startY ); - - for( var b = 0; b+1 < rs.ctrlpts.length; b += 2 ){ - // ctrl pt itself - rs.allpts.push( rs.ctrlpts[b], rs.ctrlpts[b+1] ); - - // 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 ); - } - } - - rs.allpts.push( rs.endX, rs.endY ); - - var m, mt; - if( rs.edgeType === 'bezier' ){ - rs.midX = math.qbezierAt( rs.arrowStartX, rs.ctrlpts[0], rs.arrowEndX, 0.5 ); - rs.midY = math.qbezierAt( rs.arrowStartY, rs.ctrlpts[1], rs.arrowEndY, 0.5 ); - } else if( rs.ctrlpts.length/2 % 2 === 0 ){ - m = rs.allpts.length/2 - 1; - - 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 ); - } - - } else if( rs.edgeType === 'straight' ){ - // need to calc these after endpts - rs.allpts = [ rs.startX, rs.startY, rs.endX, rs.endY ]; - - // default midpt for labels etc - rs.midX = ( rs.arrowStartX + rs.arrowEndX )/2; - rs.midY = ( rs.arrowStartY + rs.arrowEndY )/2; - - } 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( rs.segpts.length % 4 === 0 ){ - var i2 = rs.segpts.length / 2; - var i1 = i2 - 2; - - 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; - - rs.midX = rs.segpts[i1]; - rs.midY = rs.segpts[i1+1]; - } - - - } - - this.projectLines( edge ); - this.calculateArrowAngles( edge ); - this.recalculateEdgeLabelProjection( edge ); - - } - } - - for( var i = 0; i < haystackEdges.length; i++ ){ - var edge = haystackEdges[i]; - var _p = edge._private; - var style = _p.style; - var rscratch = _p.rscratch; - var rs = rscratch; - - if( !rscratch.haystack ){ - var angle = Math.random() * 2 * Math.PI; - - rscratch.source = { - x: Math.cos(angle), - y: Math.sin(angle) - }; - - var angle = Math.random() * 2 * Math.PI; - - rscratch.target = { - x: Math.cos(angle), - y: Math.sin(angle) - }; - - } - - var src = _p.source; - var tgt = _p.target; - var srcPos = src._private.position; - var tgtPos = tgt._private.position; - var srcW = src.width(); - var tgtW = tgt.width(); - var srcH = src.height(); - var tgtH = tgt.height(); - var radius = style['haystack-radius'].value; - var halfRadius = radius/2; // b/c have to half width/height - - 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 - ]; - - rs.midX = (rs.allpts[0] + rs.allpts[2])/2; - rs.midY = (rs.allpts[1] + rs.allpts[3])/2; - - // always override as haystack in case set to different type previously - rscratch.edgeType = 'haystack'; - rscratch.haystack = true; - - this.projectLines( edge ); - this.calculateArrowAngles( edge ); - this.recalculateEdgeLabelProjection( edge ); - } - - for( var i = 0 ; i < autorotateEdges.length; i++ ){ - var edge = autorotateEdges[i]; - var rs = edge._private.rscratch; - - rs.labelAngle = Math.atan( rs.midDispY / rs.midDispX ); - } - - return hashTable; -}; - -var getAngleFromDisp = function( dispX, dispY ){ - return Math.atan2( dispY, dispX ) - Math.PI/2; -}; - -BRp.calculateArrowAngles = function( edge ){ - var rs = edge._private.rscratch; - var isHaystack = rs.edgeType === 'haystack'; - var isMultibezier = rs.edgeType === 'multibezier'; - var isSegments = rs.edgeType === 'segments'; - var isCompound = rs.edgeType === 'compound'; - var isSelf = rs.edgeType === 'self'; - - // Displacement gives direction for arrowhead orientation - var dispX, dispY; - var startX, startY, endX, endY; - - var srcPos = edge.source().position(); - var tgtPos = edge.target().position(); - - 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; - } - - // source - // - - dispX = srcPos.x - startX; - dispY = srcPos.y - startY; - - rs.srcArrowAngle = getAngleFromDisp( dispX, dispY ); - - // mid target - // - - var midX = rs.midX; - var midY = rs.midY; - - if( isHaystack ){ - midX = ( startX + endX )/2; - midY = ( startY + endY )/2; - } - - dispX = endX - startX; - dispY = endY - startY; - - if( isSelf ){ - dispX = -1; - dispY = 1; - } else if( isSegments ){ - var pts = rs.allpts; - - if( pts.length / 2 % 2 === 0 ){ - var i2 = pts.length / 2; - var i1 = i2 - 2; - - 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; - - dispX = ( pts[i2] - pts[i1] ); - dispY = ( pts[i2+1] - pts[i1+1] ); - } - } else if( isMultibezier || isCompound ){ - 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 ); - - 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 - - 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 ); - } - - dispX = ( bp1x - bp0x ); - dispY = ( bp1y - bp0y ); - } - - rs.midtgtArrowAngle = getAngleFromDisp( dispX, dispY ); - - rs.midDispX = dispX; - rs.midDispY = dispY; - - // mid source - // - - dispX *= -1; - dispY *= -1; - - if( isSegments ){ - var pts = rs.allpts; - - if( pts.length / 2 % 2 === 0 ){ - // already ok - } else { - var i2 = pts.length / 2 - 1; - var i3 = i2 + 2; - - dispX = -( pts[i3] - pts[i2] ); - dispY = -( pts[i3+1] - pts[i2+1] ); - } - } - - rs.midsrcArrowAngle = getAngleFromDisp( dispX, dispY ); - - // target - // - - dispX = tgtPos.x - endX; - dispY = tgtPos.y - endY; - - rs.tgtArrowAngle = getAngleFromDisp( dispX, dispY ); -}; - - -BRp.findEndpoints = function( edge ){ - var r = this; - var intersect; - - var source = edge.source()[0]; - var target = edge.target()[0]; - - var src_p = source._private; - var tgt_p = target._private; - - var srcPos = src_p.position; - var tgtPos = tgt_p.position; - - var tgtArShape = edge._private.style['target-arrow-shape'].value; - var srcArShape = edge._private.style['source-arrow-shape'].value; - - var rs = edge._private.rscratch; - - var et = rs.edgeType; - var bezier = et === 'bezier' || et === 'multibezier' || et === 'self' || et === 'compound'; - var multi = et !== 'bezier'; - var lines = et === 'straight' || et === 'segments'; - var segments = et === 'segments'; - - var p1, p2; - - 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; - - 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 ); - - p1 = tgtArrowFromPt; - p2 = srcArrowFromPt; - } - - intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine( - tgtPos.x, - tgtPos.y, - target.outerWidth(), - target.outerHeight(), - p1[0], - p1[1], - 0 - ); - - var arrowEnd = math.shortenIntersection(intersect, p1, - r.arrowShapes[tgtArShape].spacing(edge)); - var edgeEnd = math.shortenIntersection(intersect, p1, - r.arrowShapes[tgtArShape].gap(edge)); - - rs.endX = edgeEnd[0]; - rs.endY = edgeEnd[1]; - - rs.arrowEndX = arrowEnd[0]; - rs.arrowEndY = arrowEnd[1]; - - intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine( - srcPos.x, - srcPos.y, - source.outerWidth(), - source.outerHeight(), - p2[0], - p2[1], - 0 - ); - - var arrowStart = math.shortenIntersection( - intersect, p2, - r.arrowShapes[srcArShape].spacing(edge) - ); - var edgeStart = math.shortenIntersection( - intersect, p2, - r.arrowShapes[srcArShape].gap(edge) - ); - - rs.startX = edgeStart[0]; - rs.startY = edgeStart[1]; - - rs.arrowStartX = arrowStart[0]; - rs.arrowStartY = arrowStart[1]; - - if( lines ){ - if( !is.number(rs.startX) || !is.number(rs.startY) || !is.number(rs.endX) || !is.number(rs.endY) ){ - rs.badLine = true; - } else { - rs.badLine = false; - } - } -}; - -BRp.getArrowWidth = BRp.getArrowHeight = function(edgeWidth) { - var cache = this.arrowWidthCache = this.arrowWidthCache || {}; - - var cachedVal = cache[edgeWidth]; - if( cachedVal ){ - return cachedVal; - } - - cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29); - cache[edgeWidth] = cachedVal; - - return cachedVal; -}; - -module.exports = BRp; - -},{"../../../collection/zsort":29,"../../../is":77,"../../../math":79}],57:[function(_dereq_,module,exports){ -'use strict'; - -var BRp = {}; - -BRp.getCachedImage = function(url, onLoad) { - var r = this; - var imageCache = r.imageCache = r.imageCache || {}; - - if( imageCache[url] && imageCache[url].image ){ - return imageCache[url].image; - } - - var cache = imageCache[url] = imageCache[url] || {}; - - var image = cache.image = new Image(); - image.addEventListener('load', onLoad); - image.src = url; - - return image; -}; - -module.exports = BRp; - -},{}],58:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../../is'); -var util = _dereq_('../../../util'); - -var BaseRenderer = function(){}; -var BR = BaseRenderer; -var BRp = BR.prototype; - -BRp.clientFunctions = [ 'redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl' ]; - -BRp.init = function( options ){ - var r = this; - - r.options = options; - - r.cy = options.cy; - - r.container = options.cy.container(); - - r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag - - //--Pointer-related data - r.hoverData = {down: null, last: null, - downTime: null, triggerMode: null, - dragging: false, - initialPan: [null, null], capture: false}; - - r.dragData = {possibleDragElements: []}; - - r.touchData = { - start: null, capture: false, - - // These 3 fields related to tap, taphold events - startPosition: [null, null, null, null, null, null], - singleTouchStartTime: null, - singleTouchMoved: true, - - now: [null, null, null, null, null, null], - earlier: [null, null, null, null, null, null] - }; - - r.redraws = 0; - r.showFps = options.showFps; - - 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 = true; // 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; - - r.bindings = []; - - r.registerNodeShapes(); - r.registerArrowShapes(); - r.load(); -}; - -BRp.notify = function(params) { - var types; - var r = this; - - if( is.array( params.type ) ){ - types = params.type; - - } else { - types = [ params.type ]; - } - - for( var i = 0; i < types.length; i++ ){ - var type = types[i]; - - switch( type ){ - case 'destroy': - r.destroy(); - return; - - case 'add': - case 'remove': - case 'load': - r.updateElementsCache(); - break; - - case 'viewport': - r.redrawHint('select', true); - break; - - case 'style': - r.updateCachedZSortedEles(); - break; - } - - if( type === 'load' || type === 'resize' ){ - r.invalidateContainerClientCoordsCache(); - r.matchCanvasSize(r.container); - } - } // for - - r.redrawHint('eles', true); - r.redrawHint('drag', true); - - this.startRenderLoop(); - - this.redraw(); -}; - -BRp.destroy = function(){ - this.destroyed = true; - - this.cy.stopAnimationLoop(); - - for( var i = 0; i < this.bindings.length; i++ ){ - var binding = this.bindings[i]; - var b = binding; - - b.target.removeEventListener(b.event, b.handler, b.useCapture); - } - - if( this.removeObserver ){ - this.removeObserver.disconnect(); - } - - if( this.labelCalcDiv ){ - try{ - document.body.removeChild(this.labelCalcDiv); - } catch(e){ - // ie10 issue #1014 - } - } -}; - -[ - _dereq_('./arrow-shapes'), - _dereq_('./cached-eles'), - _dereq_('./coord-ele-math'), - _dereq_('./images'), - _dereq_('./load-listeners'), - _dereq_('./node-shapes'), - _dereq_('./redraw') -].forEach(function( props ){ - util.extend( BRp, props ); -}); - -module.exports = BR; - -},{"../../../is":77,"../../../util":94,"./arrow-shapes":54,"./cached-eles":55,"./coord-ele-math":56,"./images":57,"./load-listeners":59,"./node-shapes":60,"./redraw":61}],59:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../../is'); -var util = _dereq_('../../../util'); -var Event = _dereq_('../../../event'); -var Collection = _dereq_('../../../collection'); - -var BRp = {}; - -BRp.registerBinding = function(target, event, handler, useCapture){ - this.bindings.push({ - target: target, - event: event, - handler: handler, - useCapture: useCapture - }); - - target.addEventListener(event, handler, useCapture); -}; - -BRp.nodeIsDraggable = function(node) { - if (node._private.style['opacity'].value !== 0 - && node._private.style['visibility'].value == 'visible' - && node._private.style['display'].value == 'element' - && !node.locked() - && node.grabbable() ) { - - return true; - } - - return false; -}; - -BRp.load = function() { - var r = this; - - var triggerEvents = function( target, names, e, props ){ - if( target == null ){ - target = r.cy; - } - - for( var i = 0; i < names.length; i++ ){ - var name = names[i]; - - var event = Event( e, util.extend({ type: name }, props) ); - target.trigger( event ); - } - }; - - var isMultSelKeyDown = function( e ){ - return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey - }; - - var getDragListIds = function(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]; - - opts.addToList.hasId[ ele.id() ] = true; - } - } - - listHasId = opts.addToList.hasId; - } - - return listHasId || {}; - }; - - // 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(node, opts){ - if( !node._private.cy.hasCompoundNodes() ){ - return; - } - - if( opts.inDragLayer == null && opts.addToList == null ){ return; } // nothing to do - - var listHasId = getDragListIds( opts ); - - var innerNodes = node.descendants(); - - for( var i = 0; i < innerNodes.size(); i++ ){ - var iNode = innerNodes[i]; - var _p = iNode._private; - - if( opts.inDragLayer ){ - _p.rscratch.inDragLayer = true; - } - - if( opts.addToList && !listHasId[ iNode.id() ] ){ - opts.addToList.push( iNode ); - listHasId[ iNode.id() ] = true; - - _p.grabbed = true; - } - - var edges = _p.edges; - for( var j = 0; opts.inDragLayer && j < edges.length; j++ ){ - edges[j]._private.rscratch.inDragLayer = true; - } - } - }; - - // adds the given nodes, and its edges to the drag layer - var addNodeToDrag = function(node, opts){ - - var _p = node._private; - var listHasId = getDragListIds( opts ); - - if( opts.inDragLayer ){ - _p.rscratch.inDragLayer = true; - } - - if( opts.addToList && !listHasId[ node.id() ] ){ - opts.addToList.push( node ); - listHasId[ node.id() ] = true; - - _p.grabbed = true; - } - - var edges = _p.edges; - for( var i = 0; opts.inDragLayer && i < edges.length; i++ ){ - edges[i]._private.rscratch.inDragLayer = true; - } - - addDescendantsToDrag( node, opts ); // always add to drag - - // also add nodes and edges related to the topmost ancestor - updateAncestorsInDragLayer( node, { - inDragLayer: opts.inDragLayer - } ); - }; - - var freeDraggedElements = function( draggedElements ){ - if( !draggedElements ){ return; } - - for (var i=0; i < draggedElements.length; i++) { - - var dEi_p = draggedElements[i]._private; - - if(dEi_p.group === 'nodes') { - dEi_p.rscratch.inDragLayer = false; - dEi_p.grabbed = false; - - var sEdges = dEi_p.edges; - for( var j = 0; j < sEdges.length; j++ ){ sEdges[j]._private.rscratch.inDragLayer = false; } - - // for compound nodes, also remove related nodes and edges from the drag layer - updateAncestorsInDragLayer(draggedElements[i], { inDragLayer: false }); - - } else if( dEi_p.group === 'edges' ){ - dEi_p.rscratch.inDragLayer = false; - } - - } - }; - - // 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(node, opts) { - - if( opts.inDragLayer == null && opts.addToList == null ){ return; } // nothing to do - - // find top-level parent - var parent = node; - - if( !node._private.cy.hasCompoundNodes() ){ - return; - } - - while( parent.parent().nonempty() ){ - parent = parent.parent()[0]; - } - - // no parent node: no nodes to add to the drag layer - if( parent == node ){ - return; - } - - var nodes = parent.descendants() - .merge( parent ) - .unmerge( node ) - .unmerge( node.descendants() ) - ; - - var edges = nodes.connectedEdges(); - - var listHasId = getDragListIds( opts ); - - for( var i = 0; i < nodes.size(); i++ ){ - if( opts.inDragLayer !== undefined ){ - nodes[i]._private.rscratch.inDragLayer = opts.inDragLayer; - } - - if( opts.addToList && !listHasId[ nodes[i].id() ] ){ - opts.addToList.push( nodes[i] ); - listHasId[ nodes[i].id() ] = true; - - nodes[i]._private.grabbed = true; - } - } - - for( var j = 0; opts.inDragLayer !== undefined && j < edges.length; j++ ) { - edges[j]._private.rscratch.inDragLayer = opts.inDragLayer; - } - }; - - if( typeof MutationObserver !== 'undefined' ){ - r.removeObserver = new MutationObserver(function( mutns ){ - for( var i = 0; i < mutns.length; i++ ){ - var mutn = mutns[i]; - var rNodes = mutn.removedNodes; - - if( rNodes ){ for( var j = 0; j < rNodes.length; j++ ){ - var rNode = rNodes[j]; - - if( rNode === r.container ){ - r.destroy(); - break; - } - } } - } - }); - - if( r.container.parentNode ){ - r.removeObserver.observe( r.container.parentNode, { childList: true } ); - } - } else { - r.registerBinding(r.container, 'DOMNodeRemoved', function(e){ - r.destroy(); - }); - } - - - - // auto resize - r.registerBinding(window, 'resize', util.debounce( function(e) { - r.invalidateContainerClientCoordsCache(); - - r.matchCanvasSize(r.container); - r.redrawHint('eles', true); - r.redraw(); - }, 100 ) ); - - var invalCtnrBBOnScroll = function(domEle){ - r.registerBinding(domEle, 'scroll', function(e){ - r.invalidateContainerClientCoordsCache(); - } ); - }; - - var bbCtnr = r.cy.container(); - - for( ;; ){ - - invalCtnrBBOnScroll( bbCtnr ); - - if( bbCtnr.parentNode ){ - bbCtnr = bbCtnr.parentNode; - } else { - break; - } - - } - - // stop right click menu from appearing on cy - r.registerBinding(r.container, 'contextmenu', function(e){ - e.preventDefault(); - }); - - var inBoxSelection = function(){ - return r.selection[4] !== 0; - }; - - // Primary key - r.registerBinding(r.container, 'mousedown', function(e) { - e.preventDefault(); - r.hoverData.capture = true; - r.hoverData.which = e.which; - - 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; - - r.hoverData.mdownPos = pos; - - var checkForTaphold = function(){ - r.hoverData.tapholdCancelled = false; - - clearTimeout( r.hoverData.tapholdTimeout ); - - r.hoverData.tapholdTimeout = setTimeout(function(){ - - if( r.hoverData.tapholdCancelled ){ - return; - } else { - var ele = r.hoverData.down; - - if( ele ){ - ele.trigger( Event(e, { - type: 'taphold', - cyPosition: { x: pos[0], y: pos[1] } - }) ); - } else { - cy.trigger( Event(e, { - type: 'taphold', - cyPosition: { x: pos[0], y: pos[1] } - }) ); - } - } - - }, r.tapholdDuration); - }; - - // Right click button - if( e.which == 3 ){ - - r.hoverData.cxtStarted = true; - - var cxtEvt = Event(e, { - type: 'cxttapstart', - cyPosition: { x: pos[0], y: pos[1] } - }); - - if( near ){ - near.activate(); - near.trigger( cxtEvt ); - - r.hoverData.down = near; - } else { - cy.trigger( cxtEvt ); - } - - r.hoverData.downTime = (new Date()).getTime(); - r.hoverData.cxtDragged = false; - - // Primary button - } else if (e.which == 1) { - - if( near ){ - near.activate(); - } - - // Element dragging - { - // If something is under the cursor and it is draggable, prepare to grab it - if (near != null) { - - if( r.nodeIsDraggable(near) ){ - - var grabEvent = Event(e, { - type: 'grab', - cyPosition: { x: pos[0], y: pos[1] } - }); - - if ( near.isNode() && !near.selected() ){ - - draggedElements = r.dragData.possibleDragElements = []; - addNodeToDrag( near, { addToList: draggedElements } ); - - near.trigger(grabEvent); - - } else if ( near.isNode() && near.selected() ){ - draggedElements = r.dragData.possibleDragElements = [ ]; - - var selectedNodes = cy.$(function(){ return this.isNode() && this.selected(); }); - - for( var i = 0; i < selectedNodes.length; i++ ){ - - // Only add this selected node to drag if it is draggable, eg. has nonzero opacity - if( r.nodeIsDraggable( selectedNodes[i] ) ){ - addNodeToDrag( selectedNodes[i], { addToList: draggedElements } ); - } - } - - near.trigger( grabEvent ); - } - - r.redrawHint('eles', true); - r.redrawHint('drag', true); - - } - - } - - r.hoverData.down = near; - r.hoverData.downTime = (new Date()).getTime(); - } - - triggerEvents( near, ['mousedown', 'tapstart', 'vmousedown'], e, { - cyPosition: { x: pos[0], y: pos[1] } - } ); - - if ( near == null ) { - select[4] = 1; - - r.data.bgActivePosistion = { - x: pos[0], - y: pos[1] - }; - - r.redrawHint('select', true); - - r.redraw(); - } else if( near.isEdge() ){ - select[4] = 1; // for future pan - } - - checkForTaphold(); - - } - - // Initialize selection box coordinates - select[0] = select[2] = pos[0]; - select[1] = select[3] = pos[1]; - - }, false); - - r.registerBinding(window, 'mousemove', function(e) { - var preventDefault = false; - var capture = r.hoverData.capture; - - // save cycles if mouse events aren't to be captured - if ( !capture ){ - var containerPageCoords = r.findContainerClientCoords(); - - if (e.clientX > containerPageCoords[0] && e.clientX < containerPageCoords[0] + r.canvasWidth - && e.clientY > containerPageCoords[1] && e.clientY < containerPageCoords[1] + r.canvasHeight - ) { - // inside container bounds so OK - } else { - return; - } - - var cyContainer = r.container; - var target = e.target; - var tParent = target.parentNode; - var containerIsTarget = false; - - while( tParent ){ - if( tParent === cyContainer ){ - containerIsTarget = true; - break; - } - - tParent = tParent.parentNode; - } - - if( !containerIsTarget ){ return; } // if target is outisde cy container, then this event is not for us - } - - var cy = r.cy; - var zoom = cy.zoom(); - var pos = r.projectIntoViewport(e.clientX, e.clientY); - var select = r.selection; - - var near = null; - if( !r.hoverData.draggingEles ){ - near = r.findNearestElement(pos[0], pos[1], true, false); - } - var last = r.hoverData.last; - var down = r.hoverData.down; - - var disp = [pos[0] - select[2], pos[1] - select[3]]; - - var draggedElements = r.dragData.possibleDragElements; - - var dx = select[2] - select[0]; - var dx2 = dx * dx; - var dy = select[3] - select[1]; - var dy2 = dy * dy; - var dist2 = dx2 + dy2; - var rdist2 = dist2 * zoom * zoom; - - var multSelKeyDown = isMultSelKeyDown( e ); - - r.hoverData.tapholdCancelled = true; - - var updateDragDelta = function(){ - var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || []; - - if( dragDelta.length === 0 ){ - dragDelta.push( disp[0] ); - dragDelta.push( disp[1] ); - } else { - dragDelta[0] += disp[0]; - dragDelta[1] += disp[1]; - } - }; - - - preventDefault = true; - - triggerEvents( near, ['mousemove', 'vmousemove', 'tapdrag'], e, { - cyPosition: { x: pos[0], y: pos[1] } - } ); - - // trigger context drag if rmouse down - if( r.hoverData.which === 3 ){ - var cxtEvt = Event(e, { - type: 'cxtdrag', - cyPosition: { x: pos[0], y: pos[1] } - }); - - if( down ){ - down.trigger( cxtEvt ); - } else { - cy.trigger( cxtEvt ); - } - - r.hoverData.cxtDragged = true; - - if( !r.hoverData.cxtOver || near !== r.hoverData.cxtOver ){ - - if( r.hoverData.cxtOver ){ - r.hoverData.cxtOver.trigger( Event(e, { - type: 'cxtdragout', - cyPosition: { x: pos[0], y: pos[1] } - }) ); - } - - r.hoverData.cxtOver = near; - - if( near ){ - near.trigger( Event(e, { - type: 'cxtdragover', - cyPosition: { x: pos[0], y: pos[1] } - }) ); - } - - } - - // Check if we are drag panning the entire graph - } else if (r.hoverData.dragging) { - preventDefault = true; - - if( cy.panningEnabled() && cy.userPanningEnabled() ){ - var deltaP; - - if( r.hoverData.justStartedPan ){ - var mdPos = r.hoverData.mdownPos; - - deltaP = { - x: ( pos[0] - mdPos[0] ) * zoom, - y: ( pos[1] - mdPos[1] ) * zoom - }; - - r.hoverData.justStartedPan = false; - - } else { - deltaP = { - x: disp[0] * zoom, - y: disp[1] * zoom - }; - - } - - cy.panBy( deltaP ); - - r.hoverData.dragged = true; - } - - // Needs reproject due to pan changing viewport - pos = r.projectIntoViewport(e.clientX, e.clientY); - - // Checks primary button down & out of time & mouse not moved much - } else if( - select[4] == 1 && (down == null || down.isEdge()) - ){ - - if( !r.hoverData.dragging && cy.boxSelectionEnabled() && ( multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled() ) ){ - r.data.bgActivePosistion = undefined; - r.hoverData.selecting = true; - - r.redrawHint('select', true); - r.redraw(); - - } else if( !r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled() ){ - r.hoverData.dragging = true; - r.hoverData.justStartedPan = true; - select[4] = 0; - - r.data.bgActivePosistion = { - x: pos[0], - y: pos[1] - }; - - r.redrawHint('select', true); - r.redraw(); - } - - if( down && down.isEdge() && down.active() ){ down.unactivate(); } - - } else { - if( down && down.isEdge() && down.active() ){ down.unactivate(); } - - if (near != last) { - - if (last) { - triggerEvents( last, ['mouseout', 'tapdragout'], e, { - cyPosition: { x: pos[0], y: pos[1] } - } ); - } - - if (near) { - triggerEvents( near, ['mouseover', 'tapdragover'], e, { - cyPosition: { x: pos[0], y: pos[1] } - } ); - } - - r.hoverData.last = near; - } - - if( down && down.isNode() && r.nodeIsDraggable(down) ){ - - if( rdist2 >= r.desktopTapThreshold2 ){ // then drag - - var justStartedDrag = !r.dragData.didDrag; - - if( justStartedDrag ) { - r.redrawHint('eles', true); - } - - r.dragData.didDrag = true; // indicate that we actually did drag the node - - var toTrigger = []; - - for( var i = 0; i < draggedElements.length; i++ ){ - var dEle = draggedElements[i]; - - // now, add the elements to the drag layer if not done already - if( !r.hoverData.draggingEles ){ - addNodeToDrag( dEle, { inDragLayer: true } ); - } - - // Locked nodes not draggable, as well as non-visible nodes - if( dEle.isNode() && r.nodeIsDraggable(dEle) && dEle.grabbed() ){ - var dPos = dEle._private.position; - - toTrigger.push( dEle ); - - if( is.number(disp[0]) && is.number(disp[1]) ){ - var updatePos = !dEle.isParent(); - - if( updatePos ){ - dPos.x += disp[0]; - dPos.y += disp[1]; - } - - if( justStartedDrag ){ - var dragDelta = r.hoverData.dragDelta; - - if( updatePos && is.number(dragDelta[0]) && is.number(dragDelta[1]) ){ - dPos.x += dragDelta[0]; - dPos.y += dragDelta[1]; - } - } - } - - } - } - - r.hoverData.draggingEles = true; - - var tcol = (Collection(cy, toTrigger)); - - tcol.updateCompoundBounds(); - tcol.trigger('position drag'); - - 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(); - } - } - - // prevent the dragging from triggering text selection on the page - preventDefault = true; - } - - select[2] = pos[0]; select[3] = pos[1]; - - if( preventDefault ){ - if(e.stopPropagation) e.stopPropagation(); - if(e.preventDefault) e.preventDefault(); - return false; - } - }, false); - - r.registerBinding(window, 'mouseup', function(e) { - var capture = r.hoverData.capture; - if (!capture) { return; } - r.hoverData.capture = false; - - 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 ); - - if( r.data.bgActivePosistion ){ - r.redrawHint('select', true); - r.redraw(); - } - - r.hoverData.tapholdCancelled = true; - - r.data.bgActivePosistion = undefined; // not active bg now - - if( down ){ - down.unactivate(); - } - - if( r.hoverData.which === 3 ){ - var cxtEvt = Event(e, { - type: 'cxttapend', - cyPosition: { x: pos[0], y: pos[1] } - }); - - if( down ){ - down.trigger( cxtEvt ); - } else { - cy.trigger( cxtEvt ); - } - - if( !r.hoverData.cxtDragged ){ - var cxtTap = Event(e, { - type: 'cxttap', - cyPosition: { x: pos[0], y: pos[1] } - }); - - if( down ){ - down.trigger( cxtTap ); - } else { - cy.trigger( cxtTap ); - } - } - - 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 ) - ) { - - cy.$(function(){ - return this.selected(); - }).unselect(); - - if (draggedElements.length > 0) { - r.redrawHint('eles', true); - } - - r.dragData.possibleDragElements = draggedElements = []; - } - - triggerEvents( near, ['mouseup', 'tapend', 'vmouseup'], e, { - cyPosition: { x: pos[0], y: pos[1] } - } ); - - if( - !r.dragData.didDrag // didn't move a node around - && !r.hoverData.dragged // didn't pan - ){ - triggerEvents( near, ['click', 'tap', 'vclick'], e, { - cyPosition: { x: pos[0], y: pos[1] } - } ); - } - - // Single selection - if( near == down && !r.dragData.didDrag && !r.hoverData.selecting ){ - if( near != null && near._private.selectable ){ - - 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(); - } - } - - r.redrawHint('eles', true); - } - } - - if ( r.hoverData.selecting ) { - var newlySelected = []; - var box = r.getAllInBox( select[0], select[1], select[2], select[3] ); - - r.redrawHint('select', true); - - if( box.length > 0 ) { - r.redrawHint('eles', true); - } - - for( var i = 0; i < box.length; i++ ){ - if( box[i]._private.selectable ){ - newlySelected.push( box[i] ); - } - } - - var newlySelCol = Collection( cy, newlySelected ); - - if( cy.selectionType() === 'additive' ){ - newlySelCol.select(); - } else { - if( !multSelKeyDown ){ - cy.$(':selected').unmerge( newlySelCol ).unselect(); - } - - newlySelCol.select(); - } - - // always need redraw in case eles unselectable - r.redraw(); - - } - - // Cancel drag pan - if( r.hoverData.dragging ){ - r.hoverData.dragging = false; - - r.redrawHint('select', true); - r.redrawHint('eles', true); - - r.redraw(); - } - - if (!select[4]) { - - - r.redrawHint('drag', true); - r.redrawHint('eles', true); - - freeDraggedElements( draggedElements ); - - if( down ){ down.trigger('free'); } - } - - } // else not right mouse - - select[4] = 0; r.hoverData.down = null; - - r.hoverData.cxtStarted = false; - r.hoverData.draggingEles = false; - r.hoverData.selecting = false; - r.dragData.didDrag = false; - r.hoverData.dragged = false; - r.hoverData.dragDelta = []; - - }, false); - - var wheelHandler = function(e) { - - - if( r.scrollingPage ){ return; } // while scrolling, ignore wheel-to-zoom - - 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.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection() ){ // if pan dragging or cxt dragging, wheel movements make no zoom - e.preventDefault(); - return; - } - - if( cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled() ){ - e.preventDefault(); - - r.data.wheelZooming = true; - clearTimeout( r.data.wheelTimeout ); - r.data.wheelTimeout = setTimeout(function(){ - r.data.wheelZooming = false; - - r.redrawHint('eles', true); - r.redraw(); - }, 150); - - var diff = e.deltaY / -250 || e.wheelDeltaY / 1000 || e.wheelDelta / 1000; - diff = diff * r.wheelSensitivity; - - var needsWheelFix = e.deltaMode === 1; - if( needsWheelFix ){ // fixes slow wheel events on ff/linux and ff/windows - diff *= 33; - } - - cy.zoom({ - level: cy.zoom() * Math.pow(10, diff), - renderedPosition: { x: rpos[0], y: rpos[1] } - }); - } - - }; - - // Functions to help with whether mouse wheel should trigger zooming - // -- - r.registerBinding(r.container, 'wheel', wheelHandler, true); - - // 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 - - r.registerBinding(window, 'scroll', function(e){ - r.scrollingPage = true; - - clearTimeout( r.scrollingPageTimeout ); - r.scrollingPageTimeout = setTimeout(function(){ - r.scrollingPage = false; - }, 250); - }, true); - - // Functions to help with handling mouseout/mouseover on the Cytoscape container - // Handle mouseout on Cytoscape container - r.registerBinding(r.container, 'mouseout', function(e) { - var pos = r.projectIntoViewport(e.clientX, e.clientY); - - r.cy.trigger(Event(e, { - type: 'mouseout', - cyPosition: { x: pos[0], y: pos[1] } - })); - }, false); - - r.registerBinding(r.container, 'mouseover', function(e) { - var pos = r.projectIntoViewport(e.clientX, e.clientY); - - r.cy.trigger(Event(e, { - type: 'mouseover', - cyPosition: { x: pos[0], y: pos[1] } - })); - }, false); - - 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; - - var distance = function(x1, y1, x2, y2){ - return Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) ); - }; - - var distanceSq = function(x1, y1, x2, y2){ - return (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1); - }; - - var touchstartHandler; - r.registerBinding(r.container, 'touchstart', touchstartHandler = function(e) { - r.touchData.capture = true; - r.data.bgActivePosistion = undefined; - - var cy = r.cy; - var nodes = r.getCachedNodes(); - var edges = r.getCachedEdges(); - var now = r.touchData.now; - var earlier = r.touchData.earlier; - - 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]; } - - - // record starting points for pinch-to-zoom - if( e.touches[1] ){ - - // anything in the set of dragged eles should be released - var release = function( eles ){ - for( var i = 0; i < eles.length; i++ ){ - eles[i]._private.grabbed = false; - eles[i]._private.rscratch.inDragLayer = false; - if( eles[i].active() ){ eles[i].unactivate(); } - } - }; - release(nodes); - release(edges); - - var offsets = r.findContainerClientCoords(); - offsetLeft = offsets[0]; - offsetTop = offsets[1]; - containerWidth = offsets[2]; - containerHeight = offsets[3]; - - f1x1 = e.touches[0].clientX - offsetLeft; - f1y1 = e.touches[0].clientY - offsetTop; - - f2x1 = e.touches[1].clientX - offsetLeft; - f2y1 = e.touches[1].clientY - offsetTop; - - twoFingersStartInside = - 0 <= f1x1 && f1x1 <= containerWidth - && 0 <= f2x1 && f2x1 <= containerWidth - && 0 <= f1y1 && f1y1 <= containerHeight - && 0 <= f2y1 && f2y1 <= containerHeight - ; - - var pan = cy.pan(); - var zoom = cy.zoom(); - - 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 - ]; - - // consider context tap - var cxtDistThreshold = 200; - var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold; - if( distance1Sq < cxtDistThresholdSq && !e.touches[2] ){ - - var near1 = r.findNearestElement(now[0], now[1], true, true); - var near2 = r.findNearestElement(now[2], now[3], true, true); - - if( near1 && near1.isNode() ){ - near1.activate().trigger( Event(e, { - type: 'cxttapstart', - cyPosition: { x: now[0], y: now[1] } - }) ); - r.touchData.start = near1; - - } else if( near2 && near2.isNode() ){ - near2.activate().trigger( Event(e, { - type: 'cxttapstart', - cyPosition: { x: now[0], y: now[1] } - }) ); - r.touchData.start = near2; - - } else { - cy.trigger( Event(e, { - type: 'cxttapstart', - cyPosition: { x: now[0], y: now[1] } - }) ); - r.touchData.start = null; - } - - if( r.touchData.start ){ r.touchData.start._private.grabbed = false; } - r.touchData.cxt = true; - r.touchData.cxtDragged = false; - r.data.bgActivePosistion = undefined; - - r.redraw(); - return; - - } - - } - - if (e.touches[2]) { - - } else if (e.touches[1]) { - - } else if (e.touches[0]) { - var near = r.findNearestElement(now[0], now[1], true, true); - - if (near != null) { - near.activate(); - - r.touchData.start = near; - - if( near.isNode() && r.nodeIsDraggable(near) ){ - - var draggedEles = r.dragData.touchDragEles = []; - - r.redrawHint('eles', true); - r.redrawHint('drag', true); - - if( near.selected() ){ - // reset drag elements, since near will be added again - - var selectedNodes = cy.$(function(){ - return this.isNode() && this.selected(); - }); - - for( var k = 0; k < selectedNodes.length; k++ ){ - var selectedNode = selectedNodes[k]; - - if( r.nodeIsDraggable(selectedNode) ){ - addNodeToDrag( selectedNode, { addToList: draggedEles } ); - } - } - } else { - addNodeToDrag( near, { addToList: draggedEles } ); - } - - near.trigger( Event(e, { - type: 'grab', - cyPosition: { x: now[0], y: now[1] } - }) ); - } - } - - triggerEvents( near, ['touchstart', 'tapstart', 'vmousedown'], e, { - cyPosition: { x: now[0], y: now[1] } - } ); - - if (near == null) { - r.data.bgActivePosistion = { - x: pos[0], - y: pos[1] - }; - - r.redrawHint('select', true); - r.redraw(); - } - - - // Tap, taphold - // ----- - - for (var i=0; i= factorThresholdSq || distance2Sq >= distThresholdSq ){ - r.touchData.cxt = false; - if( r.touchData.start ){ r.touchData.start.unactivate(); r.touchData.start = null; } - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); - - var cxtEvt = Event(e, { - type: 'cxttapend', - cyPosition: { x: now[0], y: now[1] } - }); - if( r.touchData.start ){ - r.touchData.start.trigger( cxtEvt ); - } else { - cy.trigger( cxtEvt ); - } - } - - } - - // context swipe - if( capture && r.touchData.cxt ){ - var cxtEvt = Event(e, { - type: 'cxtdrag', - cyPosition: { x: now[0], y: now[1] } - }); - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); - - if( r.touchData.start ){ - r.touchData.start.trigger( cxtEvt ); - } else { - cy.trigger( cxtEvt ); - } - - if( r.touchData.start ){ r.touchData.start._private.grabbed = false; } - r.touchData.cxtDragged = true; - - var near = r.findNearestElement(now[0], now[1], true, true); - - if( !r.touchData.cxtOver || near !== r.touchData.cxtOver ){ - - if( r.touchData.cxtOver ){ - r.touchData.cxtOver.trigger( Event(e, { - type: 'cxtdragout', - cyPosition: { x: now[0], y: now[1] } - }) ); - } - - r.touchData.cxtOver = near; - - if( near ){ - near.trigger( Event(e, { - type: 'cxtdragover', - cyPosition: { x: now[0], y: now[1] } - }) ); - - } - - } - - // box selection - } else if( capture && e.touches[2] && cy.boxSelectionEnabled() ){ - e.preventDefault(); - - r.data.bgActivePosistion = undefined; - - this.lastThreeTouch = +new Date(); - r.touchData.selecting = true; - - r.redrawHint('select', true); - - 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; - } - - select[4] = 1; - r.touchData.selecting = true; - - r.redraw(); - - // pinch to zoom - } else if ( capture && e.touches[1] && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled() ) { // two fingers => pinch to zoom - e.preventDefault(); - - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); - - var draggedEles = r.dragData.touchDragEles; - if( draggedEles ){ - r.redrawHint('drag', true); - - for( var i = 0; i < draggedEles.length; i++ ){ - draggedEles[i]._private.grabbed = false; - draggedEles[i]._private.rscratch.inDragLayer = false; - } - } - - // (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; - - - 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; - - if( factor != 1 && twoFingersStartInside){ - // delta finger1 - var df1x = f1x2 - f1x1; - var df1y = f1y2 - f1y1; - - // delta finger 2 - var df2x = f2x2 - f2x1; - var df2y = f2y2 - f2y1; - - // 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; - - // adjust factor by the speed multiplier - // var speed = 1.5; - // if( factor > 1 ){ - // factor = (factor - 1) * speed + 1; - // } else { - // factor = 1 - (1 - factor) * speed; - // } - - // now calculate the zoom - var zoom1 = cy.zoom(); - var zoom2 = zoom1 * factor; - var pan1 = cy.pan(); - - // 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 pan2 = { - x: -zoom2/zoom1 * (ctrx - pan1.x - tx) + ctrx, - y: -zoom2/zoom1 * (ctry - pan1.y - ty) + ctry - }; - - // remove dragged eles - if( r.touchData.start ){ - var draggedEles = r.dragData.touchDragEles; - - if( draggedEles ){ for( var i = 0; i < draggedEles.length; i++ ){ - var dEi_p = draggedEles[i]._private; - - dEi_p.grabbed = false; - dEi_p.rscratch.inDragLayer = false; - } } - - var start_p = r.touchData.start._private; - start_p.active = false; - start_p.grabbed = false; - start_p.rscratch.inDragLayer = false; - - r.redrawHint('drag', true); - - r.touchData.start - .trigger('free') - .trigger('unactivate') - ; - } - - cy.viewport({ - zoom: zoom2, - pan: pan2, - cancelOnFailedZoom: true - }); - - distance1 = distance2; - f1x1 = f1x2; - f1y1 = f1y2; - f2x1 = f2x2; - f2y1 = f2y2; - - r.pinching = true; - } - - // 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 = near || r.findNearestElement(now[0], now[1], true, true); - - if( start != null ){ - e.preventDefault(); - } - - // dragging nodes - if( start != null && start._private.group == 'nodes' && r.nodeIsDraggable(start) ){ - - if( rdist2 >= r.touchTapThreshold2 ){ // then dragging can happen - var draggedEles = r.dragData.touchDragEles; - var justStartedDrag = !r.dragData.didDrag; - - for( var k = 0; k < draggedEles.length; k++ ){ - var draggedEle = draggedEles[k]; - - if( justStartedDrag ){ - addNodeToDrag( draggedEle, { inDragLayer: true } ); - } - - if( r.nodeIsDraggable(draggedEle) && draggedEle.isNode() && draggedEle.grabbed() ){ - r.dragData.didDrag = true; - var dPos = draggedEle._private.position; - var updatePos = !draggedEle.isParent(); - - if( updatePos && is.number(disp[0]) && is.number(disp[1]) ){ - dPos.x += disp[0]; - dPos.y += disp[1]; - } - - if( justStartedDrag ){ - r.redrawHint('eles', true); - - var dragDelta = r.touchData.dragDelta; - - if( updatePos && is.number(dragDelta[0]) && is.number(dragDelta[1]) ){ - dPos.x += dragDelta[0]; - dPos.y += dragDelta[1]; - } - - } - } - } - - var tcol = Collection(cy, draggedEles); - - tcol.updateCompoundBounds(); - tcol.trigger('position drag'); - - r.hoverData.draggingEles = true; - - r.redrawHint('drag', true); - - if( - r.touchData.startPosition[0] == earlier[0] - && r.touchData.startPosition[1] == earlier[1] - ){ - - r.redrawHint('eles', true); - } - - r.redraw(); - } else { // otherise keep track of drag delta for later - var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || []; - - if( dragDelta.length === 0 ){ - dragDelta.push( disp[0] ); - dragDelta.push( disp[1] ); - } else { - dragDelta[0] += disp[0]; - dragDelta[1] += disp[1]; - } - } - } - - // touchmove - { - triggerEvents( (start || near), ['touchmove', 'tapdrag', 'vmousemove'], e, { - cyPosition: { x: now[0], y: now[1] } - } ); - - if (near != last) { - if (last) { last.trigger(Event(e, { type: 'tapdragout', cyPosition: { x: now[0], y: now[1] } })); } - if (near) { near.trigger(Event(e, { type: 'tapdragover', cyPosition: { x: now[0], y: now[1] } })); } - } - - r.touchData.last = near; - } - - // check to cancel taphold - for (var i=0;i r.touchTapThreshold2 ){ - - r.touchData.singleTouchMoved = true; - } - } - - // panning - if( - capture - && ( start == null || start.isEdge() ) - && cy.panningEnabled() && cy.userPanningEnabled() - ){ - - e.preventDefault(); - - if( r.swipePanning ){ - cy.panBy({ - x: disp[0] * zoom, - y: disp[1] * zoom - }); - - } else if( rdist2 >= r.touchTapThreshold2 ){ - r.swipePanning = true; - - cy.panBy({ - x: dx * zoom, - y: dy * zoom - }); - - if( start ){ - start.unactivate(); - - if( !r.data.bgActivePosistion ){ - r.data.bgActivePosistion = { - x: now[0], - y: now[1] - }; - } - - r.redrawHint('select', true); - - r.touchData.start = null; - } - } - - // Re-project - var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY); - now[0] = pos[0]; now[1] = pos[1]; - } - } - - for (var j=0; j 0 ) { - r.redrawHint('eles', true); - } else { - r.redraw(); - } - } - - var updateStartStyle = false; - - if( start != null ){ - start._private.active = false; - updateStartStyle = true; - start.unactivate(); - } - - if (e.touches[2]) { - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); - } else if (e.touches[1]) { - - } else if (e.touches[0]) { - - // Last touch released - } else if (!e.touches[0]) { - - r.data.bgActivePosistion = undefined; - r.redrawHint('select', true); - - var draggedEles = r.dragData.touchDragEles; - - if (start != null ) { - - var startWasGrabbed = start._private.grabbed; - - freeDraggedElements( draggedEles ); - - r.redrawHint('drag', true); - r.redrawHint('eles', true); - - if( startWasGrabbed ){ - start.trigger('free'); - } - - triggerEvents( start, ['touchend', 'tapend', 'vmouseup'], e, { - cyPosition: { x: now[0], y: now[1] } - } ); - - start.unactivate(); - - r.touchData.start = null; - - } else { - var near = r.findNearestElement(now[0], now[1], true, true); - - triggerEvents( near, ['touchend', 'tapend', 'vmouseup'], e, { - cyPosition: { x: now[0], y: now[1] } - } ); - - } - - 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; - - // 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 - ) { - - if( cy.selectionType() === 'single' ){ - cy.$(':selected').unmerge( start ).unselect(); - start.select(); - } else { - if( start.selected() ){ - start.unselect(); - } else { - start.select(); - } - } - - updateStartStyle = true; - - - r.redrawHint('eles', true); - } - - // Tap event, roughly same as mouse click event for touch - if( !r.touchData.singleTouchMoved ){ - triggerEvents( start, ['tap', 'vclick'], e, { - cyPosition: { x: now[0], y: now[1] } - } ); - } - - r.touchData.singleTouchMoved = true; - } - - for( var j = 0; j < now.length; j++ ){ earlier[j] = now[j]; } - - r.dragData.didDrag = false; // reset for next mousedown - - if( e.touches.length === 0 ){ - r.touchData.dragDelta = []; - } - - if( updateStartStyle && start ){ - start.updateStyle(false); - } - - if( e.touches.length < 2 ){ - r.pinching = false; - r.redrawHint('eles', true); - r.redraw(); - } - - //r.redraw(); - - }, false); - - // fallback compatibility layer for ms pointer events - if( typeof TouchEvent === 'undefined' ){ - - var pointers = []; - - var makeTouch = function( 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 makePointer = function( e ){ - return { - event: e, - touch: makeTouch(e) - }; - }; - - var addPointer = function( e ){ - pointers.push( makePointer(e) ); - }; - - var removePointer = function( e ){ - for( var i = 0; i < pointers.length; i++ ){ - var p = pointers[i]; - - if( p.event.pointerId === e.pointerId ){ - pointers.splice( i, 1 ); - return; - } - } - }; - - var updatePointer = function( e ){ - var p = pointers.filter(function( p ){ - return p.event.pointerId === e.pointerId; - })[0]; - - p.event = e; - p.touch = makeTouch(e); - }; - - var addTouchesToEvent = function( e ){ - e.touches = pointers.map(function( p ){ - return p.touch; - }); - }; - - r.registerBinding(r.container, 'pointerdown', function(e){ - if( e.pointerType === 'mouse' ){ return; } // mouse already handled - - e.preventDefault(); - - addPointer( e ); - - addTouchesToEvent( e ); - touchstartHandler( e ); - }); - - r.registerBinding(r.container, 'pointerup', function(e){ - if( e.pointerType === 'mouse' ){ return; } // mouse already handled - - removePointer( e ); - - addTouchesToEvent( e ); - touchendHandler( e ); - }); - - r.registerBinding(r.container, 'pointercancel', function(e){ - if( e.pointerType === 'mouse' ){ return; } // mouse already handled - - removePointer( e ); - - addTouchesToEvent( e ); - touchcancelHandler( e ); - }); - - r.registerBinding(r.container, 'pointermove', function(e){ - if( e.pointerType === 'mouse' ){ return; } // mouse already handled - - e.preventDefault(); - - updatePointer( e ); - - addTouchesToEvent( e ); - touchmoveHandler( e ); - }); - - } -}; - -module.exports = BRp; - -},{"../../../collection":23,"../../../event":42,"../../../is":77,"../../../util":94}],60:[function(_dereq_,module,exports){ -'use strict'; - -var math = _dereq_('../../../math'); - -var BRp = {}; - -BRp.registerNodeShapes = function(){ - var nodeShapes = this.nodeShapes = {}; - var renderer = this; - - nodeShapes['ellipse'] = { - name: 'ellipse', - - draw: function( context, centerX, centerY, width, height ){ - renderer.nodeShapeImpl( this.name )( context, centerX, centerY, width, height ); - }, - - intersectLine: function( nodeX, nodeY, width, height, x, y, padding ){ - return math.intersectLineEllipse( - x, y, - nodeX, - nodeY, - width / 2 + padding, - height / 2 + padding) - ; - }, - - checkPoint: function( x, y, padding, width, height, centerX, centerY ){ - x -= centerX; - y -= centerY; - - x /= (width / 2 + padding); - y /= (height / 2 + padding); - - return x*x + y*y <= 1; - } - }; - - function generatePolygon( name, points ){ - return ( nodeShapes[name] = { - name: name, - - points: points, - - draw: function( context, centerX, centerY, width, height ){ - renderer.nodeShapeImpl('polygon')( context, centerX, centerY, width, height, this.points ); - }, - - intersectLine: function( nodeX, nodeY, width, height, x, y, padding ){ - return math.polygonIntersectLine( - x, y, - this.points, - nodeX, - nodeY, - width / 2, height / 2, - padding) - ; - }, - - checkPoint: function( x, y, padding, width, height, centerX, centerY ){ - return math.pointInsidePolygon(x, y, nodeShapes[name].points, - centerX, centerY, width, height, [0, -1], padding) - ; - } - } ); - } - - generatePolygon( 'triangle', math.generateUnitNgonPointsFitToSquare(3, 0) ); - - generatePolygon( 'square', math.generateUnitNgonPointsFitToSquare(4, 0) ); - nodeShapes['rectangle'] = nodeShapes['square']; - - nodeShapes['roundrectangle'] = { - name: 'roundrectangle', - - points: math.generateUnitNgonPointsFitToSquare(4, 0), - - draw: function( context, centerX, centerY, width, height ){ - renderer.nodeShapeImpl( this.name )( context, centerX, centerY, width, height ); - }, - - intersectLine: function( nodeX, nodeY, width, height, x, y, padding ){ - return math.roundRectangleIntersectLine( - x, y, - nodeX, - nodeY, - width, height, - padding) - ; - }, - - // Looks like the width passed into this function is actually the total width / 2 - checkPoint: function( - x, y, padding, width, height, centerX, centerY ){ - - var cornerRadius = math.getRoundRectangleRadius(width, height); - - // Check hBox - if (math.pointInsidePolygon(x, y, this.points, - centerX, centerY, width, height - 2 * cornerRadius, [0, -1], padding) ){ - return true; - } - - // Check vBox - if (math.pointInsidePolygon(x, y, this.points, - centerX, centerY, width - 2 * cornerRadius, height, [0, -1], padding) ){ - return true; - } - - var checkInEllipse = function( x, y, centerX, centerY, width, height, padding ){ - x -= centerX; - y -= centerY; - - x /= (width / 2 + padding); - y /= (height / 2 + padding); - - return (x*x + y*y <= 1); - }; - - - // Check top left quarter circle - if (checkInEllipse(x, y, - centerX - width / 2 + cornerRadius, - centerY - height / 2 + cornerRadius, - cornerRadius * 2, cornerRadius * 2, padding) ){ - - return true; - } - - // Check top right quarter circle - if (checkInEllipse(x, y, - centerX + width / 2 - cornerRadius, - centerY - height / 2 + cornerRadius, - cornerRadius * 2, cornerRadius * 2, padding) ){ - - return true; - } - - // Check bottom right quarter circle - if (checkInEllipse(x, y, - centerX + width / 2 - cornerRadius, - centerY + height / 2 - cornerRadius, - cornerRadius * 2, cornerRadius * 2, padding) ){ - - return true; - } - - // Check bottom left quarter circle - if (checkInEllipse(x, y, - centerX - width / 2 + cornerRadius, - centerY + height / 2 - cornerRadius, - cornerRadius * 2, cornerRadius * 2, padding) ){ - - return true; - } - - return false; - } - }; - - generatePolygon( 'diamond', [ - 0, 1, - 1, 0, - 0, -1, - -1, 0 - ] ); - - generatePolygon( 'pentagon', math.generateUnitNgonPointsFitToSquare(5, 0) ); - - generatePolygon( 'hexagon', math.generateUnitNgonPointsFitToSquare(6, 0) ); - - generatePolygon( 'heptagon', math.generateUnitNgonPointsFitToSquare(7, 0) ); - - generatePolygon( 'octagon', math.generateUnitNgonPointsFitToSquare(8, 0) ); - - var star5Points = new Array(20); - { - var outerPoints = math.generateUnitNgonPoints(5, 0); - var innerPoints = math.generateUnitNgonPoints(5, Math.PI / 5); - - // Outer radius is 1; inner radius of star is smaller - var innerRadius = 0.5 * (3 - Math.sqrt(5)); - innerRadius *= 1.57; - - for (var i=0;i redrawLimit ? minRedrawLimit : redrawLimit; - redrawLimit = redrawLimit < maxRedrawLimit ? redrawLimit : maxRedrawLimit; - - if( r.lastDrawTime === undefined ){ r.lastDrawTime = 0; } - - var nowTime = Date.now(); - var timeElapsed = nowTime - r.lastDrawTime; - var callAfterLimit = timeElapsed >= redrawLimit; - - if( !forcedContext ){ - if( !callAfterLimit || r.currentlyDrawing ){ - r.skipFrame = true; - return; - } - } - - r.requestedFrame = true; - r.currentlyDrawing = true; - r.renderOptions = options; -}; - -BRp.startRenderLoop = function(){ - var r = this; - - var renderFn = function(){ - if( r.destroyed ){ return; } - - if( r.requestedFrame && !r.skipFrame ){ - var startTime = util.performanceNow(); - - r.render( r.renderOptions ); - - var endTime = r.lastRedrawTime = util.performanceNow(); - - if( r.averageRedrawTime === undefined ){ - r.averageRedrawTime = endTime - startTime; - } - - if( r.redrawCount === undefined ){ - r.redrawCount = 0; - } - - r.redrawCount++; - - if( r.redrawTotalTime === undefined ){ - r.redrawTotalTime = 0; - } - - var duration = endTime - startTime; - - r.redrawTotalTime += duration; - r.lastRedrawTime = duration; - - // 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; - - r.requestedFrame = false; - } - - r.skipFrame = false; - - util.requestAnimationFrame( renderFn ); - }; - - util.requestAnimationFrame( renderFn ); - -}; - -module.exports = BRp; - -},{"../../../util":94}],62:[function(_dereq_,module,exports){ -'use strict'; - -var CRp = {}; - -var impl; - -CRp.arrowShapeImpl = function( name ){ - return ( impl || (impl = { - 'polygon': function( context, points ){ - for( var i = 0; i < points.length; i++ ){ - var pt = points[i]; - - context.lineTo( pt.x, pt.y ); - } - }, - - 'triangle-backcurve': function( context, points, controlPoint ){ - var firstPt; - - for( var i = 0; i < points.length; i++ ){ - var pt = points[i]; - - if( i === 0 ){ - firstPt = pt; - } - - context.lineTo( pt.x, pt.y ); - } - - context.quadraticCurveTo( controlPoint.x, controlPoint.y, firstPt.x, firstPt.y ); - }, - - 'triangle-tee': function( context, trianglePoints, teePoints ){ - var triPts = trianglePoints; - for( var i = 0; i < triPts.length; i++ ){ - var pt = triPts[i]; - - context.lineTo( pt.x, pt.y ); - } - - var teePts = teePoints; - var firstTeePt = teePoints[0]; - context.moveTo( firstTeePt.x, firstTeePt.y ); - - for( var i = 0; i < teePts.length; i++ ){ - var pt = teePts[i]; - - context.lineTo( pt.x, pt.y ); - } - }, - - 'circle': function( context, rx, ry, r ){ - context.arc(rx, ry, r, 0, Math.PI * 2, false); - } - }) )[ name ]; -}; - -module.exports = CRp; - -},{}],63:[function(_dereq_,module,exports){ -'use strict'; - -var CRp = {}; - -CRp.drawEdge = function(context, edge, drawOverlayInstead) { - var rs = edge._private.rscratch; - var usePaths = this.usePaths(); - - // if bezier ctrl pts can not be calculated, then die - if( rs.badBezier || rs.badLine || isNaN( rs.allpts[0] ) ){ // iNaN in case edge is impossible and browser bugs (e.g. safari) - return; - } - - var style = edge._private.style; - - // Edge line width - if (style['width'].pfValue <= 0) { - return; - } - - var overlayPadding = style['overlay-padding'].pfValue; - var overlayOpacity = style['overlay-opacity'].value; - var overlayColor = style['overlay-color'].value; - - // Edge color & opacity - if( drawOverlayInstead ){ - - if( overlayOpacity === 0 ){ // exit early if no overlay - return; - } - - this.strokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity); - context.lineCap = 'round'; - - if( rs.edgeType == 'self' && !usePaths ){ - context.lineCap = 'butt'; - } - - } else { - var lineColor = style['line-color'].value; - - this.strokeStyle(context, lineColor[0], lineColor[1], lineColor[2], style.opacity.value); - - context.lineCap = 'butt'; - } - - var edgeWidth = style['width'].pfValue + (drawOverlayInstead ? 2 * overlayPadding : 0); - var lineStyle = drawOverlayInstead ? 'solid' : style['line-style'].value; - context.lineWidth = edgeWidth; - - var shadowBlur = style['shadow-blur'].pfValue; - var shadowOpacity = style['shadow-opacity'].value; - var shadowColor = style['shadow-color'].value; - var shadowOffsetX = style['shadow-offset-x'].pfValue; - var shadowOffsetY = style['shadow-offset-y'].pfValue; - - this.shadowStyle(context, shadowColor, drawOverlayInstead ? 0 : shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY); - - this.drawEdgePath( - edge, - context, - rs.allpts, - lineStyle, - edgeWidth - ); - - this.drawArrowheads(context, edge, drawOverlayInstead); - - this.shadowStyle(context, 'transparent', 0); // reset for next guy - -}; - - -CRp.drawEdgePath = function(edge, context, pts, type, width) { - var rs = edge._private.rscratch; - var canvasCxt = context; - var path; - var pathCacheHit = false; - var usePaths = this.usePaths(); - - if( usePaths ){ - var pathCacheKey = pts.join('$'); - var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey; - - if( keyMatches ){ - path = context = rs.pathCache; - pathCacheHit = true; - } else { - path = context = new Path2D(); - rs.pathCacheKey = pathCacheKey; - rs.pathCache = path; - } - } - - if( canvasCxt.setLineDash ){ // for very outofdate browsers - switch( type ){ - case 'dotted': - canvasCxt.setLineDash([ 1, 1 ]); - break; - - case 'dashed': - canvasCxt.setLineDash([ 6, 3 ]); - break; - - case 'solid': - canvasCxt.setLineDash([ ]); - break; - } - } - - if( !pathCacheHit ){ - if( context.beginPath ){ context.beginPath(); } - context.moveTo( pts[0], pts[1] ); - - switch( rs.edgeType ){ - case 'bezier': - case 'self': - case 'compound': - case 'multibezier': - if( !rs.badBezier ){ - for( var i = 2; i + 3 < pts.length; i += 4 ){ - context.quadraticCurveTo( pts[i], pts[i+1], pts[i+2], pts[i+3] ); - } - } - break; - - case 'straight': - case 'segments': - case 'haystack': - if( !rs.badLine ){ - for( var i = 2; i + 1 < pts.length; i += 2 ){ - context.lineTo( pts[i], pts[i+1] ); - } - } - break; - } - } - - context = canvasCxt; - if( usePaths ){ - context.stroke( path ); - } else { - context.stroke(); - } - - // reset any line dashes - if( context.setLineDash ){ // for very outofdate browsers - context.setLineDash([ ]); - } - -}; - -CRp.drawArrowheads = function(context, edge, drawOverlayInstead) { - if( drawOverlayInstead ){ return; } // don't do anything for overlays - - var rs = edge._private.rscratch; - var isHaystack = rs.edgeType === 'haystack'; - - if( !isHaystack ){ - this.drawArrowhead( context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle ); - } - - this.drawArrowhead( context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle ); - - this.drawArrowhead( context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle ); - - if( !isHaystack ){ - this.drawArrowhead( context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle ); - } -}; - -CRp.drawArrowhead = function( context, edge, prefix, x, y, angle ){ - if( isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null ){ return; } - - var self = this; - var style = edge._private.style; - var arrowShape = style[prefix + '-arrow-shape'].value; - - if( arrowShape === 'none' ){ - return; - } - - var gco = context.globalCompositeOperation; - - var arrowClearFill = style[prefix + '-arrow-fill'].value === 'hollow' ? 'both' : 'filled'; - var arrowFill = style[prefix + '-arrow-fill'].value; - - if( arrowShape === 'half-triangle-overshot' ){ - arrowFill = 'hollow'; - arrowClearFill = 'hollow'; - } - - if( style.opacity.value !== 1 || arrowFill === 'hollow' ){ // then extra clear is needed - context.globalCompositeOperation = 'destination-out'; - - self.fillStyle(context, 255, 255, 255, 1); - self.strokeStyle(context, 255, 255, 255, 1); - - self.drawArrowShape( edge, prefix, context, - arrowClearFill, style['width'].pfValue, style[prefix + '-arrow-shape'].value, - x, y, angle - ); - - context.globalCompositeOperation = gco; - } // otherwise, the opaque arrow clears it for free :) - - var color = style[prefix + '-arrow-color'].value; - self.fillStyle(context, color[0], color[1], color[2], style.opacity.value); - self.strokeStyle(context, color[0], color[1], color[2], style.opacity.value); - - self.drawArrowShape( edge, prefix, context, - arrowFill, style['width'].pfValue, style[prefix + '-arrow-shape'].value, - x, y, angle - ); -}; - -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; - var canvasContext = context; - var translation = { x: x, y: y }; - var size = this.getArrowWidth( edgeWidth ); - var shapeImpl = r.arrowShapes[shape]; - - if( usePaths ){ - var pathCacheKey = size + '$' + shape + '$' + angle + '$' + x + '$' + y; - rs.arrowPathCacheKey = rs.arrowPathCacheKey || {}; - rs.arrowPathCache = rs.arrowPathCache || {}; - - var alreadyCached = rs.arrowPathCacheKey[arrowType] === pathCacheKey; - if( alreadyCached ){ - path = context = rs.arrowPathCache[arrowType]; - pathCacheHit = true; - } else { - path = context = new Path2D(); - rs.arrowPathCacheKey[arrowType] = pathCacheKey; - rs.arrowPathCache[arrowType] = path; - } - } - - if( context.beginPath ){ context.beginPath(); } - - if( !pathCacheHit ){ - shapeImpl.draw(context, size, angle, translation); - } - - if( !shapeImpl.leavePathOpen && context.closePath ){ - context.closePath(); - } - - context = canvasContext; - - if( fill === 'filled' || fill === 'both' ){ - if( usePaths ){ - context.fill( path ); - } else { - context.fill(); - } - } - - if( fill === 'hollow' || fill === 'both' ){ - context.lineWidth = ( shapeImpl.matchEdgeWidth ? edgeWidth : 1 ); - context.lineJoin = 'miter'; - - if( usePaths ){ - context.stroke( path ); - } else { - context.stroke(); - } - - } -}; - -module.exports = CRp; - -},{}],64:[function(_dereq_,module,exports){ -'use strict'; - -var CRp = {}; - -CRp.safeDrawImage = function( context, img, ix, iy, iw, ih, x, y, w, h ){ - var r = this; - - try { - context.drawImage( img, ix, iy, iw, ih, x, y, w, h ); - } catch(e){ - r.data.canvasNeedsRedraw[r.NODE] = true; - r.data.canvasNeedsRedraw[r.DRAG] = true; - - r.drawingImage = true; - - r.redraw(); - } -}; - -CRp.drawInscribedImage = function(context, img, node) { - var r = this; - var nodeX = node._private.position.x; - var nodeY = node._private.position.y; - var style = node._private.style; - var fit = style['background-fit'].value; - var xPos = style['background-position-x']; - var yPos = style['background-position-y']; - var repeat = style['background-repeat'].value; - var nodeW = node.width(); - var nodeH = node.height(); - var rs = node._private.rscratch; - var clip = style['background-clip'].value; - var shouldClip = clip === 'node'; - var imgOpacity = style['background-image-opacity'].value; - - var imgW = img.width || img.cachedW; - var imgH = img.height || img.cachedH; - - // workaround for broken browsers like ie - if( null == imgW || null == imgH ){ - document.body.appendChild( img ); - - imgW = img.cachedW = img.width || img.offsetWidth; - imgH = img.cachedH = img.height || img.offsetHeight; - - document.body.removeChild( img ); - } - - var w = imgW; - var h = imgH; - - var bgW = style['background-width']; - if( bgW.value !== 'auto' ){ - if( bgW.units === '%' ){ - w = bgW.value/100 * nodeW; - } else { - w = bgW.pfValue; - } - } - - var bgH = style['background-height']; - if( bgH.value !== 'auto' ){ - if( bgH.units === '%' ){ - h = bgH.value/100 * nodeH; - } else { - h = bgH.pfValue; - } - } - - if( w === 0 || h === 0 ){ - return; // no point in drawing empty image (and chrome is broken in this case) - } - - if( fit === 'contain' ){ - var scale = Math.min( nodeW/w, nodeH/h ); - - w *= scale; - h *= scale; - - } else if( fit === 'cover' ){ - var scale = Math.max( nodeW/w, nodeH/h ); - - w *= scale; - h *= scale; - } - - var x = (nodeX - nodeW/2); // left - if( xPos.units === '%' ){ - x += (nodeW - w) * xPos.value/100; - } else { - x += xPos.pfValue; - } - - var y = (nodeY - nodeH/2); // top - if( yPos.units === '%' ){ - y += (nodeH - h) * yPos.value/100; - } else { - y += yPos.pfValue; - } - - if( rs.pathCache ){ - x -= nodeX; - y -= nodeY; - - nodeX = 0; - nodeY = 0; - } - - var gAlpha = context.globalAlpha; - - context.globalAlpha = imgOpacity; - - if( repeat === 'no-repeat' ){ - - if( shouldClip ){ - context.save(); - - if( rs.pathCache ){ - context.clip( rs.pathCache ); - } else { - r.nodeShapes[r.getNodeShape(node)].draw( - context, - nodeX, nodeY, - nodeW, nodeH); - - context.clip(); - } - } - - r.safeDrawImage( context, img, 0, 0, imgW, imgH, x, y, w, h ); - - if( shouldClip ){ - context.restore(); - } - } else { - var pattern = context.createPattern( img, repeat ); - context.fillStyle = pattern; - - r.nodeShapes[r.getNodeShape(node)].draw( - context, - nodeX, nodeY, - nodeW, nodeH); - - context.translate(x, y); - context.fill(); - context.translate(-x, -y); - } - - context.globalAlpha = gAlpha; - -}; - -module.exports = CRp; - -},{}],65:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../../is'); - -var CRp = {}; - -// Draw edge text -CRp.drawEdgeText = function(context, edge) { - var text = edge._private.style['label'].strValue; - - if( !text || text.match(/^\s+$/) ){ - return; - } - - if( this.hideEdgesOnViewport && (this.dragData.didDrag || this.pinching || this.hoverData.dragging || this.data.wheel || this.swipePanning) ){ return; } // save cycles on pinching - - var computedSize = edge._private.style['font-size'].pfValue * edge.cy().zoom(); - var minSize = edge._private.style['min-zoomed-font-size'].pfValue; - - if( computedSize < minSize ){ - return; - } - - // Calculate text draw position - - context.textAlign = 'center'; - context.textBaseline = 'middle'; - - var rs = edge._private.rscratch; - if( !is.number( rs.labelX ) || !is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered - - var style = edge._private.style; - var autorotate = style['edge-text-rotation'].strValue === 'autorotate'; - var theta; - - if( autorotate ){ - theta = rs.labelAngle; - - context.translate(rs.labelX, rs.labelY); - context.rotate(theta); - - this.drawText(context, edge, 0, 0); - - context.rotate(-theta); - context.translate(-rs.labelX, -rs.labelY); - } else { - this.drawText(context, edge, rs.labelX, rs.labelY); - } - -}; - -// Draw node text -CRp.drawNodeText = function(context, node) { - var text = node._private.style['label'].strValue; - - if ( !text || text.match(/^\s+$/) ) { - return; - } - - var computedSize = node._private.style['font-size'].pfValue * node.cy().zoom(); - var minSize = node._private.style['min-zoomed-font-size'].pfValue; - - if( computedSize < minSize ){ - return; - } - - // this.recalculateNodeLabelProjection( node ); - - var textHalign = node._private.style['text-halign'].strValue; - var textValign = node._private.style['text-valign'].strValue; - var rs = node._private.rscratch; - if( !is.number( rs.labelX ) || !is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered - - switch( textHalign ){ - case 'left': - context.textAlign = 'right'; - break; - - case 'right': - context.textAlign = 'left'; - break; - - default: // e.g. center - context.textAlign = 'center'; - } - - switch( textValign ){ - case 'top': - context.textBaseline = 'bottom'; - break; - - case 'bottom': - context.textBaseline = 'top'; - break; - - default: // e.g. center - context.textBaseline = 'middle'; - } - - this.drawText(context, node, rs.labelX, rs.labelY); -}; - -CRp.getFontCache = function(context){ - var cache; - - this.fontCaches = this.fontCaches || []; - - for( var i = 0; i < this.fontCaches.length; i++ ){ - cache = this.fontCaches[i]; - - if( cache.context === context ){ - return cache; - } - } - - cache = { - context: context - }; - this.fontCaches.push(cache); - - return cache; -}; - -// set up canvas context with font -// returns transformed text string -CRp.setupTextStyle = function( context, element ){ - // Font style - var parentOpacity = element.effectiveOpacity(); - var style = element._private.style; - var labelStyle = style['font-style'].strValue; - var labelSize = style['font-size'].pfValue + 'px'; - var labelFamily = style['font-family'].strValue; - var labelWeight = style['font-weight'].strValue; - var opacity = style['text-opacity'].value * style['opacity'].value * parentOpacity; - var outlineOpacity = style['text-outline-opacity'].value * opacity; - var color = style['color'].value; - var outlineColor = style['text-outline-color'].value; - var shadowBlur = style['text-shadow-blur'].pfValue; - var shadowOpacity = style['text-shadow-opacity'].value; - var shadowColor = style['text-shadow-color'].value; - var shadowOffsetX = style['text-shadow-offset-x'].pfValue; - var shadowOffsetY = style['text-shadow-offset-y'].pfValue; - - var fontCacheKey = element._private.fontKey; - var cache = this.getFontCache(context); - - if( cache.key !== fontCacheKey ){ - context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily; - - cache.key = fontCacheKey; - } - - var text = this.getLabelText( element ); - - // Calculate text draw position based on text alignment - - // so text outlines aren't jagged - context.lineJoin = 'round'; - - this.fillStyle(context, color[0], color[1], color[2], opacity); - - this.strokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity); - - this.shadowStyle(context, shadowColor, shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY); - - return text; -}; - -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(); -} - -// Draw text -CRp.drawText = function(context, element, textX, textY) { - var _p = element._private; - var style = _p.style; - var rstyle = _p.rstyle; - var rscratch = _p.rscratch; - var parentOpacity = element.effectiveOpacity(); - if( parentOpacity === 0 || style['text-opacity'].value === 0){ return; } - - var text = this.setupTextStyle( context, element ); - var halign = style['text-halign'].value; - var valign = style['text-valign'].value; - - if( element.isEdge() ){ - halign = 'center'; - valign = 'center'; - } - - if( element.isNode() ){ - var pLeft = style['padding-left'].pfValue; - var pRight = style['padding-right'].pfValue; - var pTop = style['padding-top'].pfValue; - var pBottom = style['padding-bottom'].pfValue; - - textX += pLeft/2; - textX -= pRight/2; - - textY += pTop/2; - textY -= pBottom/2; - } - - if ( text != null && !isNaN(textX) && !isNaN(textY)) { - var backgroundOpacity = style['text-background-opacity'].value; - var borderOpacity = style['text-border-opacity'].value; - var textBorderWidth = style['text-border-width'].pfValue; - - if( backgroundOpacity > 0 || (textBorderWidth > 0 && borderOpacity > 0) ){ - var margin = 4 + textBorderWidth/2; - - if (element.isNode()) { - //Move textX, textY to include the background margins - if (valign === 'top') { - textY -= margin; - } else if (valign === 'bottom') { - textY += margin; - } - if (halign === 'left') { - textX -= margin; - } else if (halign === 'right') { - textX += margin; - } - } - - var bgWidth = rstyle.labelWidth; - var bgHeight = rstyle.labelHeight; - var bgX = textX; - - if (halign) { - if (halign == 'center') { - bgX = bgX - bgWidth / 2; - } else if (halign == 'left') { - bgX = bgX- bgWidth; - } - } - - var bgY = textY; - - if (element.isNode()) { - if (valign == 'top') { - bgY = bgY - bgHeight; - } else if (valign == 'center') { - bgY = bgY- bgHeight / 2; - } - } else { - bgY = bgY - bgHeight / 2; - } - - if (style['edge-text-rotation'].strValue === 'autorotate') { - textY = 0; - bgWidth += 4; - bgX = textX - bgWidth / 2; - bgY = textY - bgHeight / 2; - } else { - // Adjust with border width & margin - bgX -= margin; - bgY -= margin; - bgHeight += margin*2; - bgWidth += margin*2; - } - - if( backgroundOpacity > 0 ){ - var textFill = context.fillStyle; - var textBackgroundColor = style['text-background-color'].value; - - context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')'; - var styleShape = style['text-background-shape'].strValue; - if (styleShape == 'roundrectangle') { - roundRect(context, bgX, bgY, bgWidth, bgHeight, 2); - } else { - context.fillRect(bgX,bgY,bgWidth,bgHeight); - } - context.fillStyle = textFill; - } - - if( textBorderWidth > 0 && borderOpacity > 0 ){ - var textStroke = context.strokeStyle; - var textLineWidth = context.lineWidth; - var textBorderColor = style['text-border-color'].value; - var textBorderStyle = style['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; - } - } - - context.strokeRect(bgX,bgY,bgWidth,bgHeight); - - if( textBorderStyle === 'double' ){ - var whiteWidth = textBorderWidth/2; - - context.strokeRect(bgX+whiteWidth,bgY+whiteWidth,bgWidth-whiteWidth*2,bgHeight-whiteWidth*2); - } - - if( context.setLineDash ){ // for very outofdate browsers - context.setLineDash([ ]); - } - context.lineWidth = textLineWidth; - context.strokeStyle = textStroke; - } - - } - - var lineWidth = 2 * style['text-outline-width'].pfValue; // *2 b/c the stroke is drawn centred on the middle - - if( lineWidth > 0 ){ - context.lineWidth = lineWidth; - } - - if( style['text-wrap'].value === 'wrap' ){ - var lines = rscratch.labelWrapCachedLines; - var lineHeight = rstyle.labelHeight / lines.length; - - switch( valign ){ - case 'top': - textY -= (lines.length - 1) * lineHeight; - break; - - case 'bottom': - // nothing required - break; - - default: - case 'center': - textY -= (lines.length - 1) * lineHeight / 2; - } - - for( var l = 0; l < lines.length; l++ ){ - if( lineWidth > 0 ){ - context.strokeText( lines[l], textX, textY ); - } - - context.fillText( lines[l], textX, textY ); - - textY += lineHeight; - } - - } else { - if( lineWidth > 0 ){ - context.strokeText( text, textX, textY ); - } - - context.fillText( text, textX, textY ); - } - - - this.shadowStyle(context, 'transparent', 0); // reset for next guy - } -}; - - -module.exports = CRp; - -},{"../../../is":77}],66:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../../is'); - -var CRp = {}; - -// Draw node -CRp.drawNode = function(context, node, drawOverlayInstead) { - - var r = this; - var nodeWidth, nodeHeight; - var style = node._private.style; - var rs = node._private.rscratch; - var _p = node._private; - var pos = _p.position; - - if( !is.number(pos.x) || !is.number(pos.y) ){ - return; // can't draw node with undefined position - } - - var usePaths = this.usePaths(); - var canvasContext = context; - var path; - var pathCacheHit = false; - - var overlayPadding = style['overlay-padding'].pfValue; - var overlayOpacity = style['overlay-opacity'].value; - var overlayColor = style['overlay-color'].value; - - if( drawOverlayInstead && overlayOpacity === 0 ){ // exit early if drawing overlay but none to draw - return; - } - - var parentOpacity = node.effectiveOpacity(); - if( parentOpacity === 0 ){ return; } - - nodeWidth = node.width() + style['padding-left'].pfValue + style['padding-right'].pfValue; - nodeHeight = node.height() + style['padding-top'].pfValue + style['padding-bottom'].pfValue; - - context.lineWidth = style['border-width'].pfValue; - - if( drawOverlayInstead === undefined || !drawOverlayInstead ){ - - var url = style['background-image'].value[2] || - style['background-image'].value[1]; - var image; - - if (url !== undefined) { - - // get image, and if not loaded then ask to redraw when later loaded - image = this.getCachedImage(url, function(){ - r.data.canvasNeedsRedraw[r.NODE] = true; - r.data.canvasNeedsRedraw[r.DRAG] = true; - - r.drawingImage = true; - - r.redraw(); - }); - - var prevBging = _p.backgrounding; - _p.backgrounding = !image.complete; - - if( prevBging !== _p.backgrounding ){ // update style b/c :backgrounding state changed - node.updateStyle( false ); - } - } - - // Node color & opacity - - var bgColor = style['background-color'].value; - var borderColor = style['border-color'].value; - var borderStyle = style['border-style'].value; - - this.fillStyle(context, bgColor[0], bgColor[1], bgColor[2], style['background-opacity'].value * parentOpacity); - - this.strokeStyle(context, borderColor[0], borderColor[1], borderColor[2], style['border-opacity'].value * parentOpacity); - - var shadowBlur = style['shadow-blur'].pfValue; - var shadowOpacity = style['shadow-opacity'].value; - var shadowColor = style['shadow-color'].value; - var shadowOffsetX = style['shadow-offset-x'].pfValue; - var shadowOffsetY = style['shadow-offset-y'].pfValue; - - this.shadowStyle(context, shadowColor, shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY); - - context.lineJoin = 'miter'; // so borders are square with the node shape - - if( context.setLineDash ){ // for very outofdate browsers - switch( borderStyle ){ - case 'dotted': - context.setLineDash([ 1, 1 ]); - break; - - case 'dashed': - context.setLineDash([ 4, 2 ]); - break; - - case 'solid': - case 'double': - context.setLineDash([ ]); - break; - } - } - - - var styleShape = style['shape'].strValue; - - if( usePaths ){ - var pathCacheKey = styleShape + '$' + nodeWidth +'$' + nodeHeight; - - context.translate( pos.x, pos.y ); - - if( rs.pathCacheKey === pathCacheKey ){ - path = context = rs.pathCache; - pathCacheHit = true; - } else { - path = context = new Path2D(); - rs.pathCacheKey = pathCacheKey; - rs.pathCache = path; - } - } - - if( !pathCacheHit ){ - - var npos = pos; - - if( usePaths ){ - npos = { - x: 0, - y: 0 - }; - } - - r.nodeShapes[this.getNodeShape(node)].draw( - context, - npos.x, - npos.y, - nodeWidth, - nodeHeight); - } - - context = canvasContext; - - if( usePaths ){ - context.fill( path ); - } else { - context.fill(); - } - - this.shadowStyle(context, 'transparent', 0); // reset for next guy - - if (url !== undefined) { - if( image.complete ){ - this.drawInscribedImage(context, image, node); - } - } - - var darkness = style['background-blacken'].value; - var borderWidth = style['border-width'].pfValue; - - if( this.hasPie(node) ){ - this.drawPie( context, node, parentOpacity ); - - // redraw path for blacken and border - if( darkness !== 0 || borderWidth !== 0 ){ - - if( !usePaths ){ - r.nodeShapes[this.getNodeShape(node)].draw( - context, - pos.x, - pos.y, - nodeWidth, - nodeHeight); - } - } - } - - if( darkness > 0 ){ - this.fillStyle(context, 0, 0, 0, darkness); - - if( usePaths ){ - context.fill( path ); - } else { - context.fill(); - } - - } else if( darkness < 0 ){ - this.fillStyle(context, 255, 255, 255, -darkness); - - if( usePaths ){ - context.fill( path ); - } else { - context.fill(); - } - } - - // Border width, draw border - if (borderWidth > 0) { - - if( usePaths ){ - context.stroke( path ); - } else { - context.stroke(); - } - - if( borderStyle === 'double' ){ - context.lineWidth = style['border-width'].pfValue/3; - - var gco = context.globalCompositeOperation; - context.globalCompositeOperation = 'destination-out'; - - if( usePaths ){ - context.stroke( path ); - } else { - context.stroke(); - } - - context.globalCompositeOperation = gco; - } - - } - - if( usePaths ){ - context.translate( -pos.x, -pos.y ); - } - - // reset in case we changed the border style - if( context.setLineDash ){ // for very outofdate browsers - context.setLineDash([ ]); - } - - // draw the overlay - } else { - - if( overlayOpacity > 0 ){ - this.fillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity); - - r.nodeShapes['roundrectangle'].draw( - context, - node._private.position.x, - node._private.position.y, - nodeWidth + overlayPadding * 2, - nodeHeight + overlayPadding * 2 - ); - - context.fill(); - } - } - -}; - -// does the node have at least one pie piece? -CRp.hasPie = function(node){ - node = node[0]; // ensure ele ref - - return node._private.hasPie; -}; - -CRp.drawPie = function( context, node, nodeOpacity ){ - node = node[0]; // ensure ele ref - - var _p = node._private; - var cyStyle = node.cy().style(); - var style = _p.style; - var pieSize = style['pie-size']; - var nodeW = node.width(); - var nodeH = node.height(); - var x = _p.position.x; - var y = _p.position.y; - 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(); - - if( usePaths ){ - x = 0; - y = 0; - } - - if( pieSize.units === '%' ){ - radius = radius * pieSize.value / 100; - } else if( pieSize.pfValue !== undefined ){ - radius = pieSize.pfValue / 2; - } - - for( var i = 1; i <= cyStyle.pieBackgroundN; i++ ){ // 1..N - var size = style['pie-' + i + '-background-size'].value; - var color = style['pie-' + i + '-background-color'].value; - var opacity = style['pie-' + i + '-background-opacity'].value * nodeOpacity; - var percent = size / 100; // map integer range [0, 100] to [0, 1] - - // percent can't push beyond 1 - if( percent + lastPercent > 1 ){ - percent = 1 - lastPercent; - } - - 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; - - // 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; - } - - 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); - - context.fill(); - - lastPercent += percent; - } - -}; - - -module.exports = CRp; - -},{"../../../is":77}],67:[function(_dereq_,module,exports){ -'use strict'; - -var CRp = {}; - -var util = _dereq_('../../../util'); -var math = _dereq_('../../../math'); - -var motionBlurDelay = 100; - -// var isFirefox = typeof InstallTrigger !== 'undefined'; - -CRp.getPixelRatio = function(){ - var context = this.data.contexts[0]; - - if( this.forcedPixelRatio != null ){ - return this.forcedPixelRatio; - } - - var backingStore = context.backingStorePixelRatio || - context.webkitBackingStorePixelRatio || - context.mozBackingStorePixelRatio || - context.msBackingStorePixelRatio || - context.oBackingStorePixelRatio || - context.backingStorePixelRatio || 1; - - return (window.devicePixelRatio || 1) / backingStore; -}; - -CRp.paintCache = function(context){ - var caches = this.paintCaches = this.paintCaches || []; - var needToCreateCache = true; - var cache; - - for(var i = 0; i < caches.length; i++ ){ - cache = caches[i]; - - if( cache.context === context ){ - needToCreateCache = false; - break; - } - } - - if( needToCreateCache ){ - cache = { - context: context - }; - caches.push( cache ); - } - - return cache; -}; - -CRp.fillStyle = function(context, r, g, b, a){ - context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; - - // 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; - // } -}; - -CRp.strokeStyle = function(context, r, g, b, a){ - context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; - - // 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; - // } -}; - -CRp.shadowStyle = function(context, color, opacity, blur, offsetX, offsetY){ - var zoom = this.cy.zoom(); - - var cache = this.paintCache(context); - - // don't make expensive changes to the shadow style if it's not used - if( cache.shadowOpacity === 0 && opacity === 0 ){ - return; - } - - cache.shadowOpacity = opacity; - - if (opacity > 0) { - context.shadowBlur = blur * zoom; - context.shadowColor = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + opacity + ")"; - context.shadowOffsetX = offsetX * zoom; - context.shadowOffsetY = offsetY * zoom; - } else { - context.shadowBlur = 0; - context.shadowColor = "transparent"; - } -}; - -// Resize canvas -CRp.matchCanvasSize = function(container) { - var r = this; - var data = r.data; - var width = container.clientWidth; - var height = container.clientHeight; - var pixelRatio = r.getPixelRatio(); - var mbPxRatio = r.motionBlurPxRatio; - - if( - container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || - container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG] - ){ - pixelRatio = mbPxRatio; - } - - var canvasWidth = width * pixelRatio; - var canvasHeight = height * pixelRatio; - var canvas; - - if( canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight ){ - return; // save cycles if same - } - - r.fontCaches = null; // resizing resets the style - - var canvasContainer = data.canvasContainer; - canvasContainer.style.width = width + 'px'; - canvasContainer.style.height = height + 'px'; - - for (var i = 0; i < r.CANVAS_LAYERS; i++) { - - canvas = data.canvases[i]; - - if (canvas.width !== canvasWidth || canvas.height !== canvasHeight) { - - canvas.width = canvasWidth; - canvas.height = canvasHeight; - - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - } - } - - for (var i = 0; i < r.BUFFER_COUNT; i++) { - - canvas = data.bufferCanvases[i]; - - if (canvas.width !== canvasWidth || canvas.height !== canvasHeight) { - - canvas.width = canvasWidth; - canvas.height = canvasHeight; - - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - } - } - - r.textureMult = 1; - if( pixelRatio <= 1 ){ - canvas = data.bufferCanvases[ r.TEXTURE_BUFFER ]; - - r.textureMult = 2; - canvas.width = canvasWidth * r.textureMult; - canvas.height = canvasHeight * r.textureMult; - } - - r.canvasWidth = canvasWidth; - r.canvasHeight = canvasHeight; - -}; - -CRp.renderTo = function( cxt, zoom, pan, pxRatio ){ - this.render({ - forcedContext: cxt, - forcedZoom: zoom, - forcedPan: pan, - drawAllLayers: true, - forcedPxRatio: pxRatio - }); -}; - -CRp.render = function( options ) { - options = options || util.staticEmptyObject(); - - 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 && r.motionBlurTimeout ){ - clearTimeout( r.motionBlurTimeout ); - } - - if( motionBlur ){ - if( r.mbFrames == null ){ - r.mbFrames = 0; - } - - if( !r.drawingImage ){ // image loading frames don't count towards motion blur blurry frames - r.mbFrames++; - } - - if( r.mbFrames < 3 ){ // need several frames before even high quality motionblur - motionBlurFadeEffect = false; - } - - // 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( r.clearingMotionBlur ){ - r.motionBlurPxRatio = 1; - } - - // 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 edges = r.getCachedEdges(); - 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 - }; - - 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; - - // 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; - } - - // apply pixel ratio - - effectiveZoom *= pixelRatio; - effectivePan.x *= pixelRatio; - effectivePan.y *= pixelRatio; - - var eles = { - drag: { - nodes: [], - edges: [], - eles: [] - }, - nondrag: { - nodes: [], - edges: [], - eles: [] - } - }; - - function mbclear( context, x, y, w, h ){ - var gco = context.globalCompositeOperation; - - context.globalCompositeOperation = 'destination-out'; - r.fillStyle( context, 255, 255, 255, r.motionBlurTransparency ); - context.fillRect(x, y, w, h); - - context.globalCompositeOperation = gco; - } - - function setContextTransform(context, clear){ - var ePan, eZoom, w, h; - - 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 - }; - - eZoom = zoom * mbPxRatio; - - w = r.canvasWidth * mbPxRatio; - h = r.canvasHeight * mbPxRatio; - } else { - ePan = effectivePan; - eZoom = effectiveZoom; - - w = r.canvasWidth; - h = r.canvasHeight; - } - - context.setTransform(1, 0, 0, 1, 0, 0); - - if( clear === 'motionBlur' ){ - mbclear(context, 0, 0, w, h); - } else if( !forcedContext && (clear === undefined || clear) ){ - context.clearRect(0, 0, w, h); - } - - 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 ); - } - } - - if( !textureDraw ){ - r.textureDrawLastFrame = false; - } - - if( textureDraw ){ - r.textureDrawLastFrame = true; - - var bb; - - if( !r.textureCache ){ - r.textureCache = {}; - - bb = r.textureCache.bb = cy.elements().boundingBox(); - - r.textureCache.texture = r.data.bufferCanvases[ r.TEXTURE_BUFFER ]; - - var cxt = r.data.bufferContexts[ r.TEXTURE_BUFFER ]; - - cxt.setTransform(1, 0, 0, 1, 0, 0); - cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult); - - r.render({ - forcedContext: cxt, - drawOnlyNodeLayer: true, - forcedPxRatio: pixelRatio * r.textureMult - }); - - var vp = r.textureCache.viewport = { - zoom: cy.zoom(), - pan: cy.pan(), - width: r.canvasWidth, - height: r.canvasHeight - }; - - vp.mpan = { - x: (0 - vp.pan.x)/vp.zoom, - y: (0 - vp.pan.y)/vp.zoom - }; - } - - needDraw[r.DRAG] = false; - needDraw[r.NODE] = false; - - var context = data.contexts[r.NODE]; - - var texture = r.textureCache.texture; - var vp = r.textureCache.viewport; - bb = r.textureCache.bb; - - context.setTransform(1, 0, 0, 1, 0, 0); - - if( motionBlur ){ - mbclear(context, 0, 0, vp.width, vp.height); - } else { - context.clearRect(0, 0, vp.width, vp.height); - } - - 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 ); - - var zoom = cy.zoom(); - - setContextTransform( context, false ); - - 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 vpManip = (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles); - var hideEdges = r.hideEdgesOnViewport && vpManip; - var hideLabels = r.hideLabelsOnViewport && vpManip; - - if (needDraw[r.DRAG] || needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer) { - if( hideEdges ){ - } else { - r.findEdgeControlPoints(edges); - } - - var zEles = r.getCachedZSortedEles(); - var extent = cy.extent(); - - for (var i = 0; i < zEles.length; i++) { - var ele = zEles[i]; - var list; - var bb = forcedContext ? null : ele.boundingBox(); - var insideExtent = forcedContext ? true : math.boundingBoxesIntersect( extent, bb ); - - if( !insideExtent ){ continue; } // no need to render - - if ( ele._private.rscratch.inDragLayer ) { - list = eles.drag; - } else { - list = eles.nondrag; - } - - list.eles.push( ele ); - } - - } - - - function drawElements( list, context ){ - var eles = list.eles; - - for( var i = 0; i < eles.length; i++ ){ - var ele = eles[i]; - - if( ele.isNode() ){ - r.drawNode(context, ele); - - if( !hideLabels ){ - r.drawNodeText(context, ele); - } - - r.drawNode(context, ele, true); - } else if( !hideEdges ) { - r.drawEdge(context, ele); - - if( !hideLabels ){ - r.drawEdgeText(context, ele); - } - - r.drawEdge(context, ele, true); - } - - - } - - } - - var needMbClear = []; - - needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur; - if( needMbClear[r.NODE] ){ r.clearedForMotionBlur[r.NODE] = true; } - - needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur; - if( needMbClear[r.DRAG] ){ r.clearedForMotionBlur[r.DRAG] = true; } - - 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; - - setContextTransform( context, clear ); - drawElements(eles.nondrag, context); - - if( !drawAllLayers && !motionBlur ){ - needDraw[r.NODE] = false; - } - } - - 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] ); - - setContextTransform( context, motionBlur && !useBuffer ? 'motionBlur' : undefined ); - drawElements(eles.drag, context); - - if( !drawAllLayers && !motionBlur ){ - needDraw[r.DRAG] = false; - } - } - - if( r.showFps || (!drawOnlyNodeLayer && (needDraw[r.SELECT_BOX] && !drawAllLayers)) ) { - var context = forcedContext || data.contexts[r.SELECT_BOX]; - - 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; - - 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 + ")"; - - context.fillRect( - r.selection[0], - r.selection[1], - r.selection[2] - r.selection[0], - r.selection[3] - r.selection[1]); - - 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]); - } - } - - if( data.bgActivePosistion && !r.hoverData.selecting ){ - var zoom = r.cy.zoom(); - var pos = data.bgActivePosistion; - - 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 + ")"; - - context.beginPath(); - context.arc(pos.x, pos.y, coreStyle['active-bg-size'].pfValue / zoom, 0, 2 * Math.PI); - context.fill(); - } - - var timeToRender = r.lastRedrawTime; - if( r.showFps && timeToRender ){ - timeToRender = Math.round( timeToRender ); - var fps = Math.round(1000/timeToRender); - - context.setTransform(1, 0, 0, 1, 0, 0); - - 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 maxFps = 60; - context.strokeRect(0, 30, 250, 20); - context.fillRect(0, 30, 250 * Math.min(fps/maxFps, 1), 20); - } - - if( !drawAllLayers ){ - needDraw[r.SELECT_BOX] = false; - } - } - - // 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( cxt, txt, needClear ){ - cxt.setTransform(1, 0, 0, 1, 0, 0); - - if( needClear || !motionBlurFadeEffect ){ - cxt.clearRect( 0, 0, r.canvasWidth, r.canvasHeight ); - } else { - 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; - } - - if( needDraw[r.DRAG] || needMbClear[r.DRAG] ){ - drawMotionBlur( cxtDrag, txtDrag, needMbClear[r.DRAG] ); - needDraw[r.DRAG] = false; - } - } - - r.currentlyDrawing = false; - - r.prevViewport = vp; - - if( r.clearingMotionBlur ){ - r.clearingMotionBlur = false; - r.motionBlurCleared = true; - r.motionBlur = true; - } - - if( motionBlur ){ - r.motionBlurTimeout = setTimeout(function(){ - r.motionBlurTimeout = null; - - r.clearedForMotionBlur[r.NODE] = false; - r.clearedForMotionBlur[r.DRAG] = false; - r.motionBlur = false; - r.clearingMotionBlur = !textureDraw; - r.mbFrames = 0; - - needDraw[r.NODE] = true; - needDraw[r.DRAG] = true; - - r.redraw(); - }, motionBlurDelay); - } - - r.drawingImage = false; - - - if( !forcedContext && !r.initrender ){ - r.initrender = true; - cy.trigger('initrender'); - } - - if( !forcedContext ){ - cy.triggerOnRender(); - } - -}; - -module.exports = CRp; - -},{"../../../math":79,"../../../util":94}],68:[function(_dereq_,module,exports){ -'use strict'; - - var math = _dereq_('../../../math'); - - 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, radius) { - - 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(); - }; - - var sin0 = Math.sin(0); - var cos0 = Math.cos(0); - - var sin = {}; - var cos = {}; - - var ellipseStepSize = Math.PI / 40; - - for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize ) { - sin[i] = Math.sin(i); - cos[i] = Math.cos(i); - } - - CRp.drawEllipsePath = function(context, centerX, centerY, width, height){ - if( context.beginPath ){ context.beginPath(); } - - 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; - -},{"../../../math":79}],69:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../../../is'); - -var CRp = {}; - -CRp.createBuffer = function(w, h) { - var buffer = document.createElement('canvas'); - buffer.width = w; - buffer.height = h; - - return [buffer, buffer.getContext('2d')]; -}; - -CRp.bufferCanvasImage = function( options ){ - var cy = this.cy; - var bb = cy.elements().boundingBox(); - var width = options.full ? Math.ceil(bb.w) : this.container.clientWidth; - var height = options.full ? Math.ceil(bb.h) : this.container.clientHeight; - var scale = 1; - - if( options.scale !== undefined ){ - width *= options.scale; - height *= options.scale; - - scale = options.scale; - } else if( is.number(options.maxWidth) || is.number(options.maxHeight) ){ - var maxScaleW = Infinity; - var maxScaleH = Infinity; - - if( is.number(options.maxWidth) ){ - maxScaleW = scale * options.maxWidth / width; - } - - if( is.number(options.maxHeight) ){ - maxScaleH = scale * options.maxHeight / height; - } - - scale = Math.min( maxScaleW, maxScaleH ); - - width *= scale; - height *= scale; - } - - var buffCanvas = document.createElement('canvas'); - - buffCanvas.width = width; - buffCanvas.height = height; - - buffCanvas.style.width = width + 'px'; - buffCanvas.style.height = height + 'px'; - - var buffCxt = buffCanvas.getContext('2d'); - - // Rasterize the layers, but only if container has nonzero size - if (width > 0 && height > 0) { - - buffCxt.clearRect( 0, 0, width, height ); - - if( options.bg ){ - buffCxt.fillStyle = options.bg; - buffCxt.rect( 0, 0, width, height ); - buffCxt.fill(); - } - - buffCxt.globalCompositeOperation = 'source-over'; - - if( options.full ){ // draw the full bounds of the graph - this.render({ - forcedContext: buffCxt, - drawAllLayers: true, - forcedZoom: scale, - forcedPan: { x: -bb.x1*scale, y: -bb.y1*scale }, - forcedPxRatio: 1 - }); - } else { // draw the current view - var cyPan = cy.pan(); - var pan = { - x: cyPan.x * scale, - y: cyPan.y * scale - }; - var zoom = cy.zoom() * scale; - - this.render({ - forcedContext: buffCxt, - drawAllLayers: true, - forcedZoom: zoom, - forcedPan: pan, - forcedPxRatio: 1 - }); - } - } - - return buffCanvas; -}; - -CRp.png = function( options ){ - return this.bufferCanvasImage( options ).toDataURL('image/png'); -}; - -CRp.jpg = function( options ){ - return this.bufferCanvasImage( options ).toDataURL('image/jpeg'); -}; - -module.exports = CRp; - -},{"../../../is":77}],70:[function(_dereq_,module,exports){ -/* -The canvas renderer was written by Yue Dong. - -Modifications tracked on Github. -*/ - -'use strict'; - -var util = _dereq_('../../../util'); -var is = _dereq_('../../../is'); - -var CR = CanvasRenderer; -var CRp = CanvasRenderer.prototype; - -CRp.CANVAS_LAYERS = 3; -// -CRp.SELECT_BOX = 0; -CRp.DRAG = 1; -CRp.NODE = 2; - -CRp.BUFFER_COUNT = 3; -// -CRp.TEXTURE_BUFFER = 0; -CRp.MOTIONBLUR_BUFFER_NODE = 1; -CRp.MOTIONBLUR_BUFFER_DRAG = 2; - -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), - - bufferCanvases: new Array(CRp.BUFFER_COUNT), - bufferContexts: new Array(CRp.CANVAS_LAYERS) - }; - - r.data.canvasContainer = document.createElement('div'); - var containerStyle = r.data.canvasContainer.style; - r.data.canvasContainer.setAttribute('style', '-webkit-tap-highlight-color: rgba(0,0,0,0);'); - containerStyle.position = 'relative'; - containerStyle.zIndex = '0'; - containerStyle.overflow = 'hidden'; - - var container = options.cy.container(); - container.appendChild( r.data.canvasContainer ); - container.setAttribute('style', ( container.getAttribute('style') || '' ) + '-webkit-tap-highlight-color: rgba(0,0,0,0);'); - - for (var i = 0; i < CRp.CANVAS_LAYERS; i++) { - var canvas = r.data.canvases[i] = document.createElement('canvas'); - 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); - - r.data.canvasNeedsRedraw[i] = false; - } - r.data.topCanvas = r.data.canvases[0]; - - 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'); - 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]); - } - - r.pathsEnabled = true; -} - -CRp.redrawHint = function( group, bool ){ - var r = this; - - 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; - } -}; - -// whether to use Path2D caching for drawing -var pathsImpld = typeof Path2D !== 'undefined'; - -CRp.path2dEnabled = function( on ){ - if( on === undefined ){ - return this.pathsEnabled; - } - - this.pathsEnabled = on ? true : false; -}; - -CRp.usePaths = function(){ - return pathsImpld && this.pathsEnabled; -}; - -[ - _dereq_('./arrow-shapes'), - _dereq_('./drawing-edges'), - _dereq_('./drawing-images'), - _dereq_('./drawing-label-text'), - _dereq_('./drawing-nodes'), - _dereq_('./drawing-redraw'), - _dereq_('./drawing-shapes'), - _dereq_('./export-image'), - _dereq_('./node-shapes') -].forEach(function( props ){ - util.extend( CRp, props ); -}); - -module.exports = CR; - -},{"../../../is":77,"../../../util":94,"./arrow-shapes":62,"./drawing-edges":63,"./drawing-images":64,"./drawing-label-text":65,"./drawing-nodes":66,"./drawing-redraw":67,"./drawing-shapes":68,"./export-image":69,"./node-shapes":71}],71:[function(_dereq_,module,exports){ -'use strict'; - -var CRp = {}; - -var impl; - -CRp.nodeShapeImpl = function( name ){ - var self = this; - - return ( impl || (impl = { - 'ellipse': function( context, centerX, centerY, width, height ){ - self.drawEllipsePath( context, centerX, centerY, width, height ); - }, - - 'polygon': function( context, centerX, centerY, width, height, points ){ - self.drawPolygonPath( context, centerX, centerY, width, height, points ); - }, - - 'roundrectangle': function( context, centerX, centerY, width, height ){ - self.drawRoundRectanglePath( context, centerX, centerY, width, height, 10 ); - } - }) )[ name ]; -}; - -module.exports = CRp; - -},{}],72:[function(_dereq_,module,exports){ -'use strict'; - -module.exports = [ - { name: 'null', impl: _dereq_('./null') }, - { name: 'base', impl: _dereq_('./base') }, - { name: 'canvas', impl: _dereq_('./canvas') } -]; - -},{"./base":58,"./canvas":70,"./null":73}],73:[function(_dereq_,module,exports){ -'use strict'; - -function NullRenderer(options){ - this.options = options; - this.notifications = 0; // for testing -} - -var noop = function(){}; - -NullRenderer.prototype = { - recalculateRenderedStyle: noop, - notify: function(){ this.notifications++; }, - init: noop -}; - -module.exports = NullRenderer; - -},{}],74:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('./is'); -var util = _dereq_('./util'); -var Thread = _dereq_('./thread'); -var Promise = _dereq_('./promise'); -var define = _dereq_('./define'); - -var Fabric = function( N ){ - if( !(this instanceof Fabric) ){ - return new Fabric( N ); - } - - this._private = { - pass: [] - }; - - var defN = 4; - - if( is.number(N) ){ - // then use the specified number of threads - } if( typeof navigator !== 'undefined' && navigator.hardwareConcurrency != null ){ - N = navigator.hardwareConcurrency; - } else { - try{ - N = _dereq_('os').cpus().length; - } catch( err ){ - N = defN; - } - } // TODO could use an estimation here but would the additional expense be worth it? - - for( var i = 0; i < N; i++ ){ - this[i] = new Thread(); - } - - this.length = N; -}; - -var fabfn = Fabric.prototype; // short alias - -util.extend(fabfn, { - - instanceString: function(){ return 'fabric'; }, - - // require fn in all threads - require: function( fn, as ){ - for( var i = 0; i < this.length; i++ ){ - var thread = this[i]; - - thread.require( fn, as ); - } - - return this; - }, - - // get a random thread - random: function(){ - var i = Math.round( (this.length - 1) * Math.random() ); - var thread = this[i]; - - return thread; - }, - - // run on random thread - run: function( fn ){ - var pass = this._private.pass.shift(); - - return this.random().pass( pass ).run( fn ); - }, - - // sends a random thread a message - message: function( m ){ - return this.random().message( m ); - }, - - // send all threads a message - broadcast: function( m ){ - for( var i = 0; i < this.length; i++ ){ - var thread = this[i]; - - thread.message( m ); - } - - return this; // chaining - }, - - // stop all threads - stop: function(){ - for( var i = 0; i < this.length; i++ ){ - var thread = this[i]; - - thread.stop(); - } - - return this; // chaining - }, - - // pass data to be used with .spread() etc. - pass: function( data ){ - var pass = this._private.pass; - - if( is.array(data) ){ - pass.push( data ); - } else { - throw 'Only arrays may be used with fabric.pass()'; - } - - return this; // chaining - }, - - spreadSize: function(){ - var subsize = Math.ceil( this._private.pass[0].length / this.length ); - - subsize = Math.max( 1, subsize ); // don't pass less than one ele to each thread - - return subsize; - }, - - // split the data into slices to spread the data equally among threads - spread: function( fn ){ - var self = this; - var _p = self._private; - var subsize = self.spreadSize(); // number of pass eles to handle in each thread - var pass = _p.pass.shift().concat([]); // keep a copy - var runPs = []; - - for( var i = 0; i < this.length; i++ ){ - var thread = this[i]; - var slice = pass.splice( 0, subsize ); - - var runP = thread.pass( slice ).run( fn ); - - runPs.push( runP ); - - var doneEarly = pass.length === 0; - if( doneEarly ){ break; } - } - - return Promise.all( runPs ).then(function( thens ){ - var postpass = []; - var p = 0; - - // fill postpass with the total result joined from all threads - for( var i = 0; i < thens.length; i++ ){ - var then = thens[i]; // array result from thread i - - for( var j = 0; j < then.length; j++ ){ - var t = then[j]; // array element - - postpass[ p++ ] = t; - } - } - - return postpass; - }); - }, - - // parallel version of array.map() - map: function( fn ){ - var self = this; - - self.require( fn, '_$_$_fabmap' ); - - return self.spread(function( split ){ - var mapped = []; - var origResolve = resolve; // jshint ignore:line - - resolve = function( val ){ // jshint ignore:line - mapped.push( val ); - }; - - for( var i = 0; i < split.length; i++ ){ - var oldLen = mapped.length; - var ret = _$_$_fabmap( split[i] ); // jshint ignore:line - var nothingInsdByResolve = oldLen === mapped.length; - - if( nothingInsdByResolve ){ - mapped.push( ret ); - } - } - - resolve = origResolve; // jshint ignore:line - - return mapped; - }); - - }, - - // parallel version of array.filter() - filter: function( fn ){ - var _p = this._private; - var pass = _p.pass[0]; - - return this.map( fn ).then(function( include ){ - var ret = []; - - for( var i = 0; i < pass.length; i++ ){ - var datum = pass[i]; - var incDatum = include[i]; - - if( incDatum ){ - ret.push( datum ); - } - } - - return ret; - }); - }, - - // sorts the passed array using a divide and conquer strategy - sort: function( cmp ){ - var self = this; - var P = this._private.pass[0].length; - var subsize = this.spreadSize(); - - cmp = cmp || function( a, b ){ // default comparison function - if( a < b ){ - return -1; - } else if( a > b ){ - return 1; - } - - return 0; - }; - - self.require( cmp, '_$_$_cmp' ); - - return self.spread(function( split ){ // sort each split normally - var sortedSplit = split.sort( _$_$_cmp ); // jshint ignore:line - resolve( sortedSplit ); // jshint ignore:line - - }).then(function( joined ){ - // do all the merging in the main thread to minimise data transfer - - // TODO could do merging in separate threads but would incur add'l cost of data transfer - // for each level of the merge - - var merge = function( i, j, max ){ - // don't overflow array - j = Math.min( j, P ); - max = Math.min( max, P ); - - // left and right sides of merge - var l = i; - var r = j; - - var sorted = []; - - for( var k = l; k < max; k++ ){ - - var eleI = joined[i]; - var eleJ = joined[j]; - - if( i < r && ( j >= max || cmp(eleI, eleJ) <= 0 ) ){ - sorted.push( eleI ); - i++; - } else { - sorted.push( eleJ ); - j++; - } - - } - - // in the array proper, put the sorted values - for( var k = 0; k < sorted.length; k++ ){ // kth sorted item - var index = l + k; - - joined[ index ] = sorted[k]; - } - }; - - for( var splitL = subsize; splitL < P; splitL *= 2 ){ // merge until array is "split" as 1 - - for( var i = 0; i < P; i += 2*splitL ){ - merge( i, i + splitL, i + 2*splitL ); - } - - } - - return joined; - }); - } - - -}); - -var defineRandomPasser = function( opts ){ - opts = opts || {}; - - return function( fn, arg1 ){ - var pass = this._private.pass.shift(); - - return this.random().pass( pass )[ opts.threadFn ]( fn, arg1 ); - }; -}; - -util.extend(fabfn, { - randomMap: defineRandomPasser({ threadFn: 'map' }), - - reduce: defineRandomPasser({ threadFn: 'reduce' }), - - reduceRight: defineRandomPasser({ threadFn: 'reduceRight' }) -}); - -// aliases -var fn = fabfn; -fn.promise = fn.run; -fn.terminate = fn.halt = fn.stop; -fn.include = fn.require; - -// pull in event apis -util.extend(fabfn, { - on: define.on(), - one: define.on({ unbindSelfOnTrigger: true }), - off: define.off(), - trigger: define.trigger() -}); - -define.eventAliasesOn( fabfn ); - -module.exports = Fabric; - -},{"./define":41,"./is":77,"./promise":80,"./thread":92,"./util":94,"os":undefined}],75:[function(_dereq_,module,exports){ -'use strict'; -/* jshint ignore:start */ - -// Generated by CoffeeScript 1.8.0 -(function() { - var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup; - - floor = Math.floor, min = Math.min; - - - /* - Default comparison function to be used - */ - - defaultCmp = function(x, y) { - if (x < y) { - return -1; - } - if (x > y) { - return 1; - } - return 0; - }; - - - /* - Insert item x in list a, and keep it sorted assuming a is sorted. - - If x is already in a, insert it to the right of the rightmost x. - - Optional args lo (default 0) and hi (default a.length) bound the slice - of a to be searched. - */ - - insort = function(a, x, lo, hi, cmp) { - var mid; - if (lo == null) { - lo = 0; - } - if (cmp == null) { - cmp = defaultCmp; - } - if (lo < 0) { - throw new Error('lo must be non-negative'); - } - if (hi == null) { - hi = a.length; - } - while (lo < hi) { - mid = floor((lo + hi) / 2); - if (cmp(x, a[mid]) < 0) { - hi = mid; - } else { - lo = mid + 1; - } - } - return ([].splice.apply(a, [lo, lo - lo].concat(x)), x); - }; - - - /* - Push item onto heap, maintaining the heap invariant. - */ - - heappush = function(array, item, cmp) { - if (cmp == null) { - cmp = defaultCmp; - } - array.push(item); - return _siftdown(array, 0, array.length - 1, cmp); - }; - - - /* - Pop the smallest item off the heap, maintaining the heap invariant. - */ - - heappop = function(array, cmp) { - var lastelt, returnitem; - if (cmp == null) { - cmp = defaultCmp; - } - lastelt = array.pop(); - if (array.length) { - returnitem = array[0]; - array[0] = lastelt; - _siftup(array, 0, cmp); - } else { - returnitem = lastelt; - } - return returnitem; - }; - - - /* - Pop and return the current smallest value, and add the new item. - - This is more efficient than heappop() followed by heappush(), and can be - more appropriate when using a fixed size heap. Note that the value - returned may be larger than item! That constrains reasonable use of - this routine unless written as part of a conditional replacement: - if item > array[0] - item = heapreplace(array, item) - */ - - heapreplace = function(array, item, cmp) { - var returnitem; - if (cmp == null) { - cmp = defaultCmp; - } - returnitem = array[0]; - array[0] = item; - _siftup(array, 0, cmp); - return returnitem; - }; - - - /* - Fast version of a heappush followed by a heappop. - */ - - heappushpop = function(array, item, cmp) { - var _ref; - if (cmp == null) { - cmp = defaultCmp; - } - if (array.length && cmp(array[0], item) < 0) { - _ref = [array[0], item], item = _ref[0], array[0] = _ref[1]; - _siftup(array, 0, cmp); - } - return item; - }; - - - /* - Transform list into a heap, in-place, in O(array.length) time. - */ - - heapify = function(array, cmp) { - var i, _i, _j, _len, _ref, _ref1, _results, _results1; - if (cmp == null) { - cmp = defaultCmp; - } - _ref1 = (function() { - _results1 = []; - for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); } - return _results1; - }).apply(this).reverse(); - _results = []; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - i = _ref1[_i]; - _results.push(_siftup(array, i, cmp)); - } - return _results; - }; - - - /* - Update the position of the given item in the heap. - This function should be called every time the item is being modified. - */ - - updateItem = function(array, item, cmp) { - var pos; - if (cmp == null) { - cmp = defaultCmp; - } - pos = array.indexOf(item); - if (pos === -1) { - return; - } - _siftdown(array, 0, pos, cmp); - return _siftup(array, pos, cmp); - }; - - - /* - Find the n largest elements in a dataset. - */ - - nlargest = function(array, n, cmp) { - var elem, result, _i, _len, _ref; - if (cmp == null) { - cmp = defaultCmp; - } - result = array.slice(0, n); - if (!result.length) { - return result; - } - heapify(result, cmp); - _ref = array.slice(n); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - elem = _ref[_i]; - heappushpop(result, elem, cmp); - } - return result.sort(cmp).reverse(); - }; - - - /* - Find the n smallest elements in a dataset. - */ - - nsmallest = function(array, n, cmp) { - var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results; - if (cmp == null) { - cmp = defaultCmp; - } - if (n * 10 <= array.length) { - result = array.slice(0, n).sort(cmp); - if (!result.length) { - return result; - } - los = result[result.length - 1]; - _ref = array.slice(n); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - elem = _ref[_i]; - if (cmp(elem, los) < 0) { - insort(result, elem, 0, null, cmp); - result.pop(); - los = result[result.length - 1]; - } - } - return result; - } - heapify(array, cmp); - _results = []; - for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) { - _results.push(heappop(array, cmp)); - } - return _results; - }; - - _siftdown = function(array, startpos, pos, cmp) { - var newitem, parent, parentpos; - if (cmp == null) { - cmp = defaultCmp; - } - newitem = array[pos]; - while (pos > startpos) { - parentpos = (pos - 1) >> 1; - parent = array[parentpos]; - if (cmp(newitem, parent) < 0) { - array[pos] = parent; - pos = parentpos; - continue; - } - break; - } - return array[pos] = newitem; - }; - - _siftup = function(array, pos, cmp) { - var childpos, endpos, newitem, rightpos, startpos; - if (cmp == null) { - cmp = defaultCmp; - } - endpos = array.length; - startpos = pos; - newitem = array[pos]; - childpos = 2 * pos + 1; - while (childpos < endpos) { - rightpos = childpos + 1; - if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) { - childpos = rightpos; - } - array[pos] = array[childpos]; - pos = childpos; - childpos = 2 * pos + 1; - } - array[pos] = newitem; - return _siftdown(array, startpos, pos, cmp); - }; - - Heap = (function() { - Heap.push = heappush; - - Heap.pop = heappop; - - Heap.replace = heapreplace; - - Heap.pushpop = heappushpop; - - Heap.heapify = heapify; - - Heap.updateItem = updateItem; - - Heap.nlargest = nlargest; - - Heap.nsmallest = nsmallest; - - function Heap(cmp) { - this.cmp = cmp != null ? cmp : defaultCmp; - this.nodes = []; - } - - Heap.prototype.push = function(x) { - return heappush(this.nodes, x, this.cmp); - }; - - Heap.prototype.pop = function() { - return heappop(this.nodes, this.cmp); - }; - - Heap.prototype.peek = function() { - return this.nodes[0]; - }; - - Heap.prototype.contains = function(x) { - return this.nodes.indexOf(x) !== -1; - }; - - Heap.prototype.replace = function(x) { - return heapreplace(this.nodes, x, this.cmp); - }; - - Heap.prototype.pushpop = function(x) { - return heappushpop(this.nodes, x, this.cmp); - }; - - Heap.prototype.heapify = function() { - return heapify(this.nodes, this.cmp); - }; - - Heap.prototype.updateItem = function(x) { - return updateItem(this.nodes, x, this.cmp); - }; - - Heap.prototype.clear = function() { - return this.nodes = []; - }; - - Heap.prototype.empty = function() { - return this.nodes.length === 0; - }; - - Heap.prototype.size = function() { - return this.nodes.length; - }; - - Heap.prototype.clone = function() { - var heap; - heap = new Heap(); - heap.nodes = this.nodes.slice(0); - return heap; - }; - - Heap.prototype.toArray = function() { - return this.nodes.slice(0); - }; - - Heap.prototype.insert = Heap.prototype.push; - - Heap.prototype.top = Heap.prototype.peek; - - Heap.prototype.front = Heap.prototype.peek; - - Heap.prototype.has = Heap.prototype.contains; - - Heap.prototype.copy = Heap.prototype.clone; - - return Heap; - - })(); - - (function(root, factory) { - if (typeof define === 'function' && define.amd) { - return define([], factory); - } else if (typeof exports === 'object') { - return module.exports = factory(); - } else { - return root.Heap = factory(); - } - })(this, function() { - return Heap; - }); - -}).call(this); - -/* jshint ignore:end */ - -},{}],76:[function(_dereq_,module,exports){ -'use strict'; - -var window = _dereq_('./window'); -var is = _dereq_('./is'); -var Core = _dereq_('./core'); -var extension = _dereq_('./extension'); -var registerJquery = _dereq_('./jquery-plugin'); -var Stylesheet = _dereq_('./stylesheet'); -var Thread = _dereq_('./thread'); -var Fabric = _dereq_('./fabric'); - -var cytoscape = function( options ){ // jshint ignore:line - // if no options specified, use default - if( options === undefined ){ - options = {}; - } - - // create instance - if( is.plainObject( options ) ){ - return new Core( options ); - } - - // allow for registration of extensions - else if( is.string( options ) ) { - return extension.apply(extension, arguments); - } -}; - -// replaced by build system -cytoscape.version = '2.5.1'; - -// try to register w/ jquery -if( window && window.jQuery ){ - registerJquery( window.jQuery, cytoscape ); -} - -// expose register api -cytoscape.registerJquery = function( jQuery ){ - registerJquery( jQuery, cytoscape ); -}; - -// expose public apis (mostly for extensions) -cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet; -cytoscape.thread = cytoscape.Thread = Thread; -cytoscape.fabric = cytoscape.Fabric = Fabric; - -module.exports = cytoscape; - -},{"./core":34,"./extension":43,"./fabric":74,"./is":77,"./jquery-plugin":78,"./stylesheet":91,"./thread":92,"./window":100}],77:[function(_dereq_,module,exports){ -'use strict'; - -var window = _dereq_('./window'); -var navigator = window ? window.navigator : null; - -var typeofstr = typeof ''; -var typeofobj = typeof {}; -var typeoffn = typeof function(){}; -var typeofhtmlele = typeof HTMLElement; - -var instanceStr = function( obj ){ - return obj && obj.instanceString && is.fn( obj.instanceString ) ? obj.instanceString() : null; -}; - -var is = { - defined: function(obj){ - return obj != null; // not undefined or null - }, - - string: function(obj){ - return obj != null && typeof obj == typeofstr; - }, - - fn: function(obj){ - return obj != null && typeof obj === typeoffn; - }, - - array: function(obj){ - return Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array; - }, - - plainObject: function(obj){ - return obj != null && typeof obj === typeofobj && !is.array(obj) && obj.constructor === Object; - }, - - object: function(obj){ - return obj != null && typeof obj === typeofobj; - }, - - number: function(obj){ - return obj != null && typeof obj === typeof 1 && !isNaN(obj); - }, - - integer: function( obj ){ - return is.number(obj) && Math.floor(obj) === obj; - }, - - bool: function(obj){ - return obj != null && typeof obj === typeof true; - }, - - htmlElement: function(obj){ - if( 'undefined' === typeofhtmlele ){ - return undefined; - } else { - return null != obj && obj instanceof HTMLElement; - } - }, - - elementOrCollection: function(obj){ - return is.element(obj) || is.collection(obj); - }, - - element: function(obj){ - return instanceStr(obj) === 'collection' && obj._private.single; - }, - - collection: function(obj){ - return instanceStr(obj) === 'collection' && !obj._private.single; - }, - - core: function(obj){ - return instanceStr(obj) === 'core'; - }, - - style: function(obj){ - return instanceStr(obj) === 'style'; - }, - - stylesheet: function(obj){ - return instanceStr(obj) === 'stylesheet'; - }, - - event: function(obj){ - return instanceStr(obj) === 'event'; - }, - - thread: function(obj){ - return instanceStr(obj) === 'thread'; - }, - - fabric: function(obj){ - return instanceStr(obj) === 'fabric'; - }, - - emptyString: function(obj){ - if( !obj ){ // null is empty - return true; - } else if( is.string(obj) ){ - if( obj === '' || obj.match(/^\s+$/) ){ - return true; // empty string is empty - } - } - - return false; // otherwise, we don't know what we've got - }, - - nonemptyString: function(obj){ - if( obj && is.string(obj) && obj !== '' && !obj.match(/^\s+$/) ){ - return true; - } - - return false; - }, - - domElement: function(obj){ - if( typeof HTMLElement === 'undefined' ){ - return false; // we're not in a browser so it doesn't matter - } else { - return obj instanceof HTMLElement; - } - }, - - boundingBox: function(obj){ - return is.plainObject(obj) && - is.number(obj.x1) && is.number(obj.x2) && - is.number(obj.y1) && is.number(obj.y2) - ; - }, - - promise: function(obj){ - return is.object(obj) && is.fn(obj.then); - }, - - touch: function(){ - return window && ( ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch ); - }, - - gecko: function(){ - return typeof InstallTrigger !== 'undefined' || ('MozAppearance' in document.documentElement.style); - }, - - webkit: function(){ - return typeof webkitURL !== 'undefined' || ('WebkitAppearance' in document.documentElement.style); - }, - - chromium: function(){ - return typeof chrome !== 'undefined'; - }, - - khtml: function(){ - return navigator && navigator.vendor.match(/kde/i); // probably a better way to detect this... - }, - - khtmlEtc: function(){ - return is.khtml() || is.webkit() || is.chromium(); - }, - - ms: function(){ - return navigator && navigator.userAgent.match(/msie|trident|edge/i); // probably a better way to detect this... - }, - - windows: function(){ - return navigator && navigator.appVersion.match(/Win/i); - }, - - mac: function(){ - return navigator && navigator.appVersion.match(/Mac/i); - }, - - linux: function(){ - return navigator && navigator.appVersion.match(/Linux/i); - }, - - unix: function(){ - return navigator && navigator.appVersion.match(/X11/i); - } -}; - -module.exports = is; - -},{"./window":100}],78:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('./is'); - -var cyReg = function( $ele ){ - var d = $ele[0]._cyreg = $ele[0]._cyreg || {}; - - return d; -}; - -var registerJquery = function( $, cytoscape ){ - if( !$ ){ return; } // no jquery => don't need this - - if( $.fn.cytoscape ){ return; } // already registered - - // allow calls on a jQuery selector by proxying calls to $.cytoscape - // e.g. $("#foo").cytoscape(options) => $.cytoscape(options) on #foo - $.fn.cytoscape = function(opts){ - var $this = $(this); - - // get object - if( opts === 'get' ){ - return cyReg( $this ).cy; - } - - // bind to ready - else if( is.fn(opts) ){ - - var ready = opts; - var cy = cyReg( $this ).cy; - - if( cy && cy.isReady() ){ // already ready so just trigger now - cy.trigger('ready', [], ready); - - } else { // not yet ready, so add to readies list - var data = cyReg( $this ); - var readies = data.readies = data.readies || []; - - readies.push( ready ); - } - - } - - // proxy to create instance - else if( is.plainObject(opts) ){ - return $this.each(function(){ - var options = $.extend({}, opts, { - container: $(this)[0] - }); - - cytoscape(options); - }); - } - }; - - // allow access to the global cytoscape object under jquery for legacy reasons - $.cytoscape = cytoscape; - - // use short alias (cy) if not already defined - if( $.fn.cy == null && $.cy == null ){ - $.fn.cy = $.fn.cytoscape; - $.cy = $.cytoscape; - } -}; - -module.exports = registerJquery; - -},{"./is":77}],79:[function(_dereq_,module,exports){ -'use strict'; - -var math = {}; - -math.signum = function(x){ - if( x > 0 ){ - return 1; - } else if( x < 0 ){ - return -1; - } else { - return 0; - } -}; - -math.distance = function( p1, p2 ){ - return Math.sqrt( math.sqDistance(p1, p2) ); -}; - -math.sqDistance = function( p1, p2 ){ - var dx = p2.x - p1.x; - var dy = p2.y - p1.y; - - return dx*dx + dy*dy; -}; - -// from http://en.wikipedia.org/wiki/Bézier_curve#Quadratic_curves -math.qbezierAt = function(p0, p1, p2, t){ - return (1 - t)*(1 - t)*p0 + 2*(1 - t)*t*p1 + t*t*p2; -}; - -math.qbezierPtAt = function(p0, p1, p2, t){ - return { - x: math.qbezierAt( p0.x, p1.x, p2.x, t ), - y: math.qbezierAt( p0.y, p1.y, p2.y, t ) - }; -}; - -// makes a full bb (x1, y1, x2, y2, w, h) from implicit params -math.makeBoundingBox = function( bb ){ - if( bb.x1 != null && bb.y1 != null ){ - if( bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1 ){ - return { - x1: bb.x1, - y1: bb.y1, - x2: bb.x2, - y2: bb.y2, - w: bb.x2 - bb.x1, - h: bb.y2 - bb.y1 - }; - } else if( bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0 ){ - return { - x1: bb.x1, - y1: bb.y1, - x2: bb.x1 + bb.w, - y2: bb.y1 + bb.h, - w: bb.w, - h: bb.h - }; - } - } -}; - -math.boundingBoxesIntersect = function( bb1, bb2 ){ - // case: one bb to right of other - if( bb1.x1 > bb2.x2 ){ return false; } - if( bb2.x1 > bb1.x2 ){ return false; } - - // case: one bb to left of other - if( bb1.x2 < bb2.x1 ){ return false; } - if( bb2.x2 < bb1.x1 ){ return false; } - - // case: one bb above other - if( bb1.y2 < bb2.y1 ){ return false; } - if( bb2.y2 < bb1.y1 ){ return false; } - - // case: one bb below other - if( bb1.y1 > bb2.y2 ){ return false; } - if( bb2.y1 > bb1.y2 ){ return false; } - - // otherwise, must have some overlap - return true; -}; - -math.inBoundingBox = function( bb, x, y ){ - return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2; -}; - -math.pointInBoundingBox = function( bb, pt ){ - return this.inBoundingBox( bb, pt.x, pt.y ); -}; - -math.roundRectangleIntersectLine = function( - x, y, nodeX, nodeY, width, height, padding) { - - var cornerRadius = this.getRoundRectangleRadius(width, height); - - var halfWidth = width / 2; - var halfHeight = height / 2; - - // Check intersections with straight line segments - var straightLineIntersections; - - // Top segment, left to right - { - var topStartX = nodeX - halfWidth + cornerRadius - padding; - var topStartY = nodeY - halfHeight - padding; - var topEndX = nodeX + halfWidth - cornerRadius + padding; - var topEndY = topStartY; - - straightLineIntersections = this.finiteLinesIntersect( - x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false); - - if (straightLineIntersections.length > 0) { - return straightLineIntersections; - } - } - - // Right segment, top to bottom - { - var rightStartX = nodeX + halfWidth + padding; - var rightStartY = nodeY - halfHeight + cornerRadius - padding; - var rightEndX = rightStartX; - var rightEndY = nodeY + halfHeight - cornerRadius + padding; - - straightLineIntersections = this.finiteLinesIntersect( - x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false); - - if (straightLineIntersections.length > 0) { - return straightLineIntersections; - } - } - - // Bottom segment, left to right - { - var bottomStartX = nodeX - halfWidth + cornerRadius - padding; - var bottomStartY = nodeY + halfHeight + padding; - var bottomEndX = nodeX + halfWidth - cornerRadius + padding; - var bottomEndY = bottomStartY; - - straightLineIntersections = this.finiteLinesIntersect( - x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false); - - if (straightLineIntersections.length > 0) { - return straightLineIntersections; - } - } - - // Left segment, top to bottom - { - var leftStartX = nodeX - halfWidth - padding; - var leftStartY = nodeY - halfHeight + cornerRadius - padding; - var leftEndX = leftStartX; - var leftEndY = nodeY + halfHeight - cornerRadius + padding; - - straightLineIntersections = this.finiteLinesIntersect( - x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false); - - if (straightLineIntersections.length > 0) { - return straightLineIntersections; - } - } - - // Check intersections with arc segments - var arcIntersections; - - // Top Left - { - var topLeftCenterX = nodeX - halfWidth + cornerRadius; - var topLeftCenterY = nodeY - halfHeight + cornerRadius; - arcIntersections = this.intersectLineCircle( - x, y, nodeX, nodeY, - topLeftCenterX, topLeftCenterY, cornerRadius + padding); - - // Ensure the intersection is on the desired quarter of the circle - if (arcIntersections.length > 0 - && arcIntersections[0] <= topLeftCenterX - && arcIntersections[1] <= topLeftCenterY) { - return [arcIntersections[0], arcIntersections[1]]; - } - } - - // Top Right - { - var topRightCenterX = nodeX + halfWidth - cornerRadius; - var topRightCenterY = nodeY - halfHeight + cornerRadius; - arcIntersections = this.intersectLineCircle( - x, y, nodeX, nodeY, - topRightCenterX, topRightCenterY, cornerRadius + padding); - - // Ensure the intersection is on the desired quarter of the circle - if (arcIntersections.length > 0 - && arcIntersections[0] >= topRightCenterX - && arcIntersections[1] <= topRightCenterY) { - return [arcIntersections[0], arcIntersections[1]]; - } - } - - // Bottom Right - { - var bottomRightCenterX = nodeX + halfWidth - cornerRadius; - var bottomRightCenterY = nodeY + halfHeight - cornerRadius; - arcIntersections = this.intersectLineCircle( - x, y, nodeX, nodeY, - bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); - - // Ensure the intersection is on the desired quarter of the circle - if (arcIntersections.length > 0 - && arcIntersections[0] >= bottomRightCenterX - && arcIntersections[1] >= bottomRightCenterY) { - return [arcIntersections[0], arcIntersections[1]]; - } - } - - // Bottom Left - { - var bottomLeftCenterX = nodeX - halfWidth + cornerRadius; - var bottomLeftCenterY = nodeY + halfHeight - cornerRadius; - arcIntersections = this.intersectLineCircle( - x, y, nodeX, nodeY, - bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); - - // Ensure the intersection is on the desired quarter of the circle - if (arcIntersections.length > 0 - && arcIntersections[0] <= bottomLeftCenterX - && arcIntersections[1] >= bottomLeftCenterY) { - return [arcIntersections[0], arcIntersections[1]]; - } - } - - return []; // if nothing -}; - -math.inLineVicinity = function(x, y, lx1, ly1, lx2, ly2, tolerance){ - var t = tolerance; - - var x1 = Math.min(lx1, lx2); - var x2 = Math.max(lx1, lx2); - var y1 = Math.min(ly1, ly2); - var y2 = Math.max(ly1, ly2); - - return x1 - t <= x && x <= x2 + t - && y1 - t <= y && y <= y2 + t; -}; - -math.inBezierVicinity = function( - x, y, x1, y1, x2, y2, x3, y3, tolerance) { - - var bb = { - x1: Math.min( x1, x3, x2 ) - tolerance, - x2: Math.max( x1, x3, x2 ) + tolerance, - y1: Math.min( y1, y3, y2 ) - tolerance, - y2: Math.max( y1, y3, y2 ) + tolerance - }; - - // if outside the rough bounding box for the bezier, then it can't be a hit - if( x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2 ){ - // console.log('bezier out of rough bb') - return false; - } else { - // console.log('do more expensive check'); - return true; - } - -}; - -math.solveCubic = function(a, b, c, d, result) { - - // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where - // r is the real component, i is the imaginary component - - // An implementation of the Cardano method from the year 1545 - // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots - - b /= a; - c /= a; - d /= a; - - var discriminant, q, r, dum1, s, t, term1, r13; - - q = (3.0 * c - (b * b)) / 9.0; - r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b)); - r /= 54.0; - - discriminant = q * q * q + r * r; - result[1] = 0; - term1 = (b / 3.0); - - if (discriminant > 0) { - s = r + Math.sqrt(discriminant); - s = ((s < 0) ? -Math.pow(-s, (1.0 / 3.0)) : Math.pow(s, (1.0 / 3.0))); - t = r - Math.sqrt(discriminant); - t = ((t < 0) ? -Math.pow(-t, (1.0 / 3.0)) : Math.pow(t, (1.0 / 3.0))); - result[0] = -term1 + s + t; - term1 += (s + t) / 2.0; - result[4] = result[2] = -term1; - term1 = Math.sqrt(3.0) * (-t + s) / 2; - result[3] = term1; - result[5] = -term1; - return; - } - - result[5] = result[3] = 0; - - if (discriminant === 0) { - r13 = ((r < 0) ? -Math.pow(-r, (1.0 / 3.0)) : Math.pow(r, (1.0 / 3.0))); - result[0] = -term1 + 2.0 * r13; - result[4] = result[2] = -(r13 + term1); - return; - } - - q = -q; - dum1 = q * q * q; - dum1 = Math.acos(r / Math.sqrt(dum1)); - r13 = 2.0 * Math.sqrt(q); - result[0] = -term1 + r13 * Math.cos(dum1 / 3.0); - result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0); - result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0); - - return; -}; - -math.sqDistanceToQuadraticBezier = function( - x, y, x1, y1, x2, y2, x3, y3) { - - // Find minimum distance by using the minimum of the distance - // function between the given point and the curve - - // This gives the coefficients of the resulting cubic equation - // whose roots tell us where a possible minimum is - // (Coefficients are divided by 4) - - var a = 1.0 * x1*x1 - 4*x1*x2 + 2*x1*x3 + 4*x2*x2 - 4*x2*x3 + x3*x3 - + y1*y1 - 4*y1*y2 + 2*y1*y3 + 4*y2*y2 - 4*y2*y3 + y3*y3; - - var b = 1.0 * 9*x1*x2 - 3*x1*x1 - 3*x1*x3 - 6*x2*x2 + 3*x2*x3 - + 9*y1*y2 - 3*y1*y1 - 3*y1*y3 - 6*y2*y2 + 3*y2*y3; - - var c = 1.0 * 3*x1*x1 - 6*x1*x2 + x1*x3 - x1*x + 2*x2*x2 + 2*x2*x - x3*x - + 3*y1*y1 - 6*y1*y2 + y1*y3 - y1*y + 2*y2*y2 + 2*y2*y - y3*y; - - var d = 1.0 * x1*x2 - x1*x1 + x1*x - x2*x - + y1*y2 - y1*y1 + y1*y - y2*y; - - // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a); - - var roots = []; - - // Use the cubic solving algorithm - this.solveCubic(a, b, c, d, roots); - - var zeroThreshold = 0.0000001; - - var params = []; - - for (var index = 0; index < 6; index += 2) { - if (Math.abs(roots[index + 1]) < zeroThreshold - && roots[index] >= 0 - && roots[index] <= 1.0) { - params.push(roots[index]); - } - } - - params.push(1.0); - params.push(0.0); - - var minDistanceSquared = -1; - var closestParam; - - var curX, curY, distSquared; - for (var i = 0; i < params.length; i++) { - curX = Math.pow(1.0 - params[i], 2.0) * x1 - + 2.0 * (1 - params[i]) * params[i] * x2 - + params[i] * params[i] * x3; - - curY = Math.pow(1 - params[i], 2.0) * y1 - + 2 * (1.0 - params[i]) * params[i] * y2 - + params[i] * params[i] * y3; - - distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); - // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared)); - if (minDistanceSquared >= 0) { - if (distSquared < minDistanceSquared) { - minDistanceSquared = distSquared; - closestParam = params[i]; - } - } else { - minDistanceSquared = distSquared; - closestParam = params[i]; - } - } - - return minDistanceSquared; -}; - -math.sqDistanceToFiniteLine = function(x, y, x1, y1, x2, y2) { - var offset = [x - x1, y - y1]; - var line = [x2 - x1, y2 - y1]; - - var lineSq = line[0] * line[0] + line[1] * line[1]; - var hypSq = offset[0] * offset[0] + offset[1] * offset[1]; - - var dotProduct = offset[0] * line[0] + offset[1] * line[1]; - var adjSq = dotProduct * dotProduct / lineSq; - - if (dotProduct < 0) { - return hypSq; - } - - if (adjSq > lineSq) { - return (x - x2) * (x - x2) + (y - y2) * (y - y2); - } - - return hypSq - adjSq; -}; - -math.pointInsidePolygonPoints = function(x, y, points){ - var x1, y1, x2, y2; - var y3; - - // Intersect with vertical line through (x, y) - var up = 0; - var down = 0; - for (var i = 0; i < points.length / 2; i++) { - - x1 = points[i * 2]; - y1 = points[i * 2 + 1]; - - if (i + 1 < points.length / 2) { - x2 = points[(i + 1) * 2]; - y2 = points[(i + 1) * 2 + 1]; - } else { - x2 = points[(i + 1 - points.length / 2) * 2]; - y2 = points[(i + 1 - points.length / 2) * 2 + 1]; - } - - if (x1 == x && x2 == x) { - - } else if ((x1 >= x && x >= x2) - || (x1 <= x && x <= x2)) { - - y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1; - - if (y3 > y) { - up++; - } - - if (y3 < y) { - down++; - } - - } else { - continue; - } - - } - - if (up % 2 === 0) { - return false; - } else { - return true; - } -}; - -math.pointInsidePolygon = function( - x, y, basePoints, centerX, centerY, width, height, direction, padding) { - - //var direction = arguments[6]; - var transformedPoints = new Array(basePoints.length); - - // Gives negative angle - var angle; - - if( direction[0] != null ){ - angle = Math.atan(direction[1] / direction[0]); - - if (direction[0] < 0) { - angle = angle + Math.PI / 2; - } else { - angle = -angle - Math.PI / 2; - } - } else { - angle = direction; - } - - var cos = Math.cos(-angle); - var sin = Math.sin(-angle); - - // console.log("base: " + basePoints); - for (var i = 0; i < transformedPoints.length / 2; i++) { - transformedPoints[i * 2] = - width / 2 * (basePoints[i * 2] * cos - - basePoints[i * 2 + 1] * sin); - - transformedPoints[i * 2 + 1] = - height / 2 * (basePoints[i * 2 + 1] * cos - + basePoints[i * 2] * sin); - - transformedPoints[i * 2] += centerX; - transformedPoints[i * 2 + 1] += centerY; - } - - var points; - - if (padding > 0) { - var expandedLineSet = this.expandPolygon( - transformedPoints, - -padding); - - points = this.joinLines(expandedLineSet); - } else { - points = transformedPoints; - } - - return math.pointInsidePolygonPoints( x, y, points ); -}; - -math.joinLines = function(lineSet) { - - var vertices = new Array(lineSet.length / 2); - - var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY; - var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY; - - for (var i = 0; i < lineSet.length / 4; i++) { - currentLineStartX = lineSet[i * 4]; - currentLineStartY = lineSet[i * 4 + 1]; - currentLineEndX = lineSet[i * 4 + 2]; - currentLineEndY = lineSet[i * 4 + 3]; - - if (i < lineSet.length / 4 - 1) { - nextLineStartX = lineSet[(i + 1) * 4]; - nextLineStartY = lineSet[(i + 1) * 4 + 1]; - nextLineEndX = lineSet[(i + 1) * 4 + 2]; - nextLineEndY = lineSet[(i + 1) * 4 + 3]; - } else { - nextLineStartX = lineSet[0]; - nextLineStartY = lineSet[1]; - nextLineEndX = lineSet[2]; - nextLineEndY = lineSet[3]; - } - - var intersection = this.finiteLinesIntersect( - currentLineStartX, currentLineStartY, - currentLineEndX, currentLineEndY, - nextLineStartX, nextLineStartY, - nextLineEndX, nextLineEndY, - true); - - vertices[i * 2] = intersection[0]; - vertices[i * 2 + 1] = intersection[1]; - } - - return vertices; -}; - -math.expandPolygon = function(points, pad) { - - var expandedLineSet = new Array(points.length * 2); - - var currentPointX, currentPointY, nextPointX, nextPointY; - - for (var i = 0; i < points.length / 2; i++) { - currentPointX = points[i * 2]; - currentPointY = points[i * 2 + 1]; - - if (i < points.length / 2 - 1) { - nextPointX = points[(i + 1) * 2]; - nextPointY = points[(i + 1) * 2 + 1]; - } else { - nextPointX = points[0]; - nextPointY = points[1]; - } - - // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY] - - // Assume CCW polygon winding - - var offsetX = (nextPointY - currentPointY); - var offsetY = -(nextPointX - currentPointX); - - // Normalize - var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY); - var normalizedOffsetX = offsetX / offsetLength; - var normalizedOffsetY = offsetY / offsetLength; - - expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad; - expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad; - expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad; - expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad; - } - - return expandedLineSet; -}; - -math.intersectLineEllipse = function( - x, y, centerX, centerY, ellipseWradius, ellipseHradius) { - - var dispX = centerX - x; - var dispY = centerY - y; - - dispX /= ellipseWradius; - dispY /= ellipseHradius; - - var len = Math.sqrt(dispX * dispX + dispY * dispY); - - var newLength = len - 1; - - if (newLength < 0) { - return []; - } - - var lenProportion = newLength / len; - - return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y]; -}; - -// Returns intersections of increasing distance from line's start point -math.intersectLineCircle = function( - x1, y1, x2, y2, centerX, centerY, radius) { - - // Calculate d, direction vector of line - var d = [x2 - x1, y2 - y1]; // Direction vector of line - var c = [centerX, centerY]; // Center of circle - var f = [x1 - centerX, y1 - centerY]; - - var a = d[0] * d[0] + d[1] * d[1]; - var b = 2 * (f[0] * d[0] + f[1] * d[1]); - var c = (f[0] * f[0] + f[1] * f[1]) - radius * radius ; - - var discriminant = b*b-4*a*c; - - if (discriminant < 0) { - return []; - } - - var t1 = (-b + Math.sqrt(discriminant)) / (2 * a); - var t2 = (-b - Math.sqrt(discriminant)) / (2 * a); - - var tMin = Math.min(t1, t2); - var tMax = Math.max(t1, t2); - var inRangeParams = []; - - if (tMin >= 0 && tMin <= 1) { - inRangeParams.push(tMin); - } - - if (tMax >= 0 && tMax <= 1) { - inRangeParams.push(tMax); - } - - if (inRangeParams.length === 0) { - return []; - } - - var nearIntersectionX = inRangeParams[0] * d[0] + x1; - var nearIntersectionY = inRangeParams[0] * d[1] + y1; - - if (inRangeParams.length > 1) { - - if (inRangeParams[0] == inRangeParams[1]) { - return [nearIntersectionX, nearIntersectionY]; - } else { - - var farIntersectionX = inRangeParams[1] * d[0] + x1; - var farIntersectionY = inRangeParams[1] * d[1] + y1; - - return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY]; - } - - } else { - return [nearIntersectionX, nearIntersectionY]; - } - -}; - -math.findCircleNearPoint = function(centerX, centerY, - radius, farX, farY) { - - var displacementX = farX - centerX; - var displacementY = farY - centerY; - var distance = Math.sqrt(displacementX * displacementX - + displacementY * displacementY); - - var unitDisplacementX = displacementX / distance; - var unitDisplacementY = displacementY / distance; - - return [centerX + unitDisplacementX * radius, - centerY + unitDisplacementY * radius]; -}; - -math.findMaxSqDistanceToOrigin = function(points) { - var maxSqDistance = 0.000001; - var sqDistance; - - for (var i = 0; i < points.length / 2; i++) { - - sqDistance = points[i * 2] * points[i * 2] - + points[i * 2 + 1] * points[i * 2 + 1]; - - if (sqDistance > maxSqDistance) { - maxSqDistance = sqDistance; - } - } - - return maxSqDistance; -}; - -math.finiteLinesIntersect = function( - x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) { - - var ua_t = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); - var ub_t = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3); - var u_b = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); - - if (u_b !== 0) { - var ua = ua_t / u_b; - var ub = ub_t / u_b; - - if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) { - return [x1 + ua * (x2 - x1), y1 + ua * (y2 - y1)]; - - } else { - if (!infiniteLines) { - return []; - } else { - return [x1 + ua * (x2 - x1), y1 + ua * (y2 - y1)]; - } - } - } else { - if (ua_t === 0 || ub_t === 0) { - - // Parallel, coincident lines. Check if overlap - - // Check endpoint of second line - if ([x1, x2, x4].sort()[1] === x4) { - return [x4, y4]; - } - - // Check start point of second line - if ([x1, x2, x3].sort()[1] === x3) { - return [x3, y3]; - } - - // Endpoint of first line - if ([x3, x4, x2].sort()[1] === x2) { - return [x2, y2]; - } - - return []; - } else { - - // Parallel, non-coincident - return []; - } - } -}; - -math.polygonIntersectLine = function( - x, y, basePoints, centerX, centerY, width, height, padding) { - - var intersections = []; - var intersection; - - var transformedPoints = new Array(basePoints.length); - - for (var i = 0; i < transformedPoints.length / 2; i++) { - transformedPoints[i * 2] = basePoints[i * 2] * width + centerX; - transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY; - } - - var points; - - if (padding > 0) { - var expandedLineSet = math.expandPolygon( - transformedPoints, - -padding); - - points = math.joinLines(expandedLineSet); - } else { - points = transformedPoints; - } - // var points = transformedPoints; - - var currentX, currentY, nextX, nextY; - - for (var i = 0; i < points.length / 2; i++) { - - currentX = points[i * 2]; - currentY = points[i * 2 + 1]; - - if (i < points.length / 2 - 1) { - nextX = points[(i + 1) * 2]; - nextY = points[(i + 1) * 2 + 1]; - } else { - nextX = points[0]; - nextY = points[1]; - } - - intersection = this.finiteLinesIntersect( - x, y, centerX, centerY, - currentX, currentY, - nextX, nextY); - - if (intersection.length !== 0) { - intersections.push(intersection[0], intersection[1]); - } - } - - return intersections; -}; - -math.shortenIntersection = function( - intersection, offset, amount) { - - var disp = [intersection[0] - offset[0], intersection[1] - offset[1]]; - - var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]); - - var lenRatio = (length - amount) / length; - - if (lenRatio < 0) { - lenRatio = 0.00001; - } - - return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]]; -}; - -math.generateUnitNgonPointsFitToSquare = function(sides, rotationRadians) { - var points = math.generateUnitNgonPoints(sides, rotationRadians); - points = math.fitPolygonToSquare(points); - - return points; -}; - -math.fitPolygonToSquare = function(points){ - var x, y; - var sides = points.length/2; - var minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; - - for (var i = 0; i < sides; i++) { - x = points[2 * i]; - y = points[2 * i + 1]; - - minX = Math.min( minX, x ); - maxX = Math.max( maxX, x ); - minY = Math.min( minY, y ); - maxY = Math.max( maxY, y ); - } - - // stretch factors - var sx = 2 / (maxX - minX); - var sy = 2 / (maxY - minY); - - for (var i = 0; i < sides; i++){ - x = points[2 * i] = points[2 * i] * sx; - y = points[2 * i + 1] = points[2 * i + 1] * sy; - - minX = Math.min( minX, x ); - maxX = Math.max( maxX, x ); - minY = Math.min( minY, y ); - maxY = Math.max( maxY, y ); - } - - if( minY < -1 ){ - for (var i = 0; i < sides; i++){ - y = points[2 * i + 1] = points[2 * i + 1] + (-1 -minY); - } - } - - return points; -}; - -math.generateUnitNgonPoints = function(sides, rotationRadians) { - - var increment = 1.0 / sides * 2 * Math.PI; - var startAngle = sides % 2 === 0 ? - Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0; - // console.log(nodeShapes['square']); - startAngle += rotationRadians; - - var points = new Array(sides * 2); - - var currentAngle, x, y; - for (var i = 0; i < sides; i++) { - currentAngle = i * increment + startAngle; - - x = points[2 * i] = Math.cos(currentAngle);// * (1 + i/2); - y = points[2 * i + 1] = Math.sin(-currentAngle);// * (1 + i/2); - } - - return points; -}; - -math.getRoundRectangleRadius = function(width, height) { - - // Set the default radius, unless half of width or height is smaller than default - return Math.min(width / 4, height / 4, 8); -}; - -module.exports = math; - -},{}],80:[function(_dereq_,module,exports){ -// internal, minimal Promise impl s.t. apis can return promises in old envs -// based on thenable (http://github.com/rse/thenable) - -'use strict'; - -/* promise states [Promises/A+ 2.1] */ -var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */ -var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */ -var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */ - -/* promise object constructor */ -var api = function (executor) { - /* optionally support non-constructor/plain-function call */ - if (!(this instanceof api)) - return new api(executor); - - /* initialize object */ - this.id = "Thenable/1.0.7"; - this.state = STATE_PENDING; /* initial state */ - this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */ - this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */ - this.onFulfilled = []; /* initial handlers */ - this.onRejected = []; /* initial handlers */ - - /* provide optional information-hiding proxy */ - this.proxy = { - then: this.then.bind(this) - }; - - /* support optional executor function */ - if (typeof executor === "function") - executor.call(this, this.fulfill.bind(this), this.reject.bind(this)); -}; - -/* promise API methods */ -api.prototype = { - /* promise resolving methods */ - fulfill: function (value) { return deliver(this, STATE_FULFILLED, "fulfillValue", value); }, - reject: function (value) { return deliver(this, STATE_REJECTED, "rejectReason", value); }, - - /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */ - then: function (onFulfilled, onRejected) { - var curr = this; - var next = new api(); /* [Promises/A+ 2.2.7] */ - curr.onFulfilled.push( - resolver(onFulfilled, next, "fulfill")); /* [Promises/A+ 2.2.2/2.2.6] */ - curr.onRejected.push( - resolver(onRejected, next, "reject" )); /* [Promises/A+ 2.2.3/2.2.6] */ - execute(curr); - return next.proxy; /* [Promises/A+ 2.2.7, 3.3] */ - } -}; - -/* deliver an action */ -var deliver = function (curr, state, name, value) { - if (curr.state === STATE_PENDING) { - curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */ - curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */ - execute(curr); - } - return curr; -}; - -/* execute all handlers */ -var execute = function (curr) { - if (curr.state === STATE_FULFILLED) - execute_handlers(curr, "onFulfilled", curr.fulfillValue); - else if (curr.state === STATE_REJECTED) - execute_handlers(curr, "onRejected", curr.rejectReason); -}; - -/* execute particular set of handlers */ -var execute_handlers = function (curr, name, value) { - /* global setImmediate: true */ - /* global setTimeout: true */ - - /* short-circuit processing */ - if (curr[name].length === 0) - return; - - /* iterate over all handlers, exactly once */ - var handlers = curr[name]; - curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */ - var func = function () { - for (var i = 0; i < handlers.length; i++) - handlers[i](value); /* [Promises/A+ 2.2.5] */ - }; - - /* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */ - if (typeof setImmediate === "function") - setImmediate(func); - else - setTimeout(func, 0); -}; - -/* generate a resolver function */ -var resolver = function (cb, next, method) { - return function (value) { - if (typeof cb !== "function") /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */ - next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */ - else { - var result; - try { result = cb(value); } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */ - catch (e) { - next.reject(e); /* [Promises/A+ 2.2.7.2] */ - return; - } - resolve(next, result); /* [Promises/A+ 2.2.7.1] */ - } - }; -}; - -/* "Promise Resolution Procedure" */ /* [Promises/A+ 2.3] */ -var resolve = function (promise, x) { - /* sanity check arguments */ /* [Promises/A+ 2.3.1] */ - if (promise === x || promise.proxy === x) { - promise.reject(new TypeError("cannot resolve promise with itself")); - return; - } - - /* surgically check for a "then" method - (mainly to just call the "getter" of "then" only once) */ - var then; - if ((typeof x === "object" && x !== null) || typeof x === "function") { - try { then = x.then; } /* [Promises/A+ 2.3.3.1, 3.5] */ - catch (e) { - promise.reject(e); /* [Promises/A+ 2.3.3.2] */ - return; - } - } - - /* handle own Thenables [Promises/A+ 2.3.2] - and similar "thenables" [Promises/A+ 2.3.3] */ - if (typeof then === "function") { - var resolved = false; - try { - /* call retrieved "then" method */ /* [Promises/A+ 2.3.3.3] */ - then.call(x, - /* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */ - function (y) { - if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */ - if (y === x) /* [Promises/A+ 3.6] */ - promise.reject(new TypeError("circular thenable chain")); - else - resolve(promise, y); - }, - - /* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */ - function (r) { - if (resolved) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */ - promise.reject(r); - } - ); - } - catch (e) { - if (!resolved) /* [Promises/A+ 2.3.3.3.3] */ - promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */ - } - return; - } - - /* handle other values */ - promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */ -}; - -// use native promises where possible -var Promise = typeof Promise === 'undefined' ? api : Promise; - -// so we always have Promise.all() -Promise.all = Promise.all || function( ps ){ - return new Promise(function( resolveAll, rejectAll ){ - var vals = new Array( ps.length ); - var doneCount = 0; - - var fulfill = function( i, val ){ - vals[i] = val; - doneCount++; - - if( doneCount === ps.length ){ - resolveAll( vals ); - } - }; - - for( var i = 0; i < ps.length; i++ ){ - (function( i ){ - var p = ps[i]; - var isPromise = p.then != null; - - if( isPromise ){ - p.then(function( val ){ - fulfill( i, val ); - }, function( err ){ - rejectAll( err ); - }); - } else { - var val = p; - fulfill( i, val ); - } - })( i ); - } - - }); -}; - -module.exports = Promise; - -},{}],81:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('./is'); -var util = _dereq_('./util'); - -var Selector = function( onlyThisGroup, selector ){ - - if( !(this instanceof Selector) ){ - return new Selector(onlyThisGroup, selector); - } - - if( selector === undefined && onlyThisGroup !== undefined ){ - selector = onlyThisGroup; - onlyThisGroup = undefined; - } - - var self = this; - - self._private = { - selectorText: null, - invalid: true - }; - - if( !selector || ( is.string(selector) && selector.match(/^\s*$/) ) ){ - - if( onlyThisGroup == null ){ - // ignore - self.length = 0; - } else { - self[0] = newQuery(); - self[0].group = onlyThisGroup; - self.length = 1; - } - - } else if( is.elementOrCollection( selector ) ){ - var collection = selector.collection(); - - self[0] = newQuery(); - self[0].collection = collection; - self.length = 1; - - } else if( is.fn( selector ) ) { - self[0] = newQuery(); - self[0].filter = selector; - self.length = 1; - - } else if( is.string( selector ) ){ - - // the current subject in the query - var currentSubject = null; - - // storage for parsed queries - var newQuery = function(){ - 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 - }; - }; - - // tokens in the query language - var tokens = { - metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]', // chars we need to escape in var 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: '\\$' - }; - 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) - - // 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(str){ - return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function(match, $1, offset, original){ - return $1; - }); - }; - - // add @ variants to comparatorOp - var ops = tokens.comparatorOp.split('|'); - for( var i = 0; i < ops.length; i++ ){ - var op = ops[i]; - tokens.comparatorOp += '|@' + op; - } - - // add ! variants to comparatorOp - var ops = tokens.comparatorOp.split('|'); - for( var i = 0; i < ops.length; i++ ){ - var op = ops[i]; - - if( op.indexOf('!') >= 0 ){ continue; } // skip ops that explicitly contain ! - if( op === '=' ){ continue; } // skip = b/c != is explicitly defined - - tokens.comparatorOp += '|\\!' + op; - } - - // 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 self[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: '(node|edge|\\*)', - populate: function( group ){ - this.group = group == "*" ? group : group + 's'; - } - }, - - { - name: 'state', - query: true, - // NB: if one colon selector is a substring of another from its start, place the longer one first - // e.g. :foobar|:foo - regex: '(:selected|:unselected|:locked|:unlocked|:visible|:hidden|:transparent|:grabbed|:free|:removed|:inside|:grabbable|:ungrabbable|:animated|:unanimated|:selectable|:unselectable|:orphan|:nonorphan|:parent|:child|:loop|:simple|:active|:inactive|:touch|:backgrounding|:nonbackgrounding)', - populate: function( state ){ - this.colonSelectors.push( state ); - } - }, - - { - name: 'id', - query: true, - regex: '\\#('+ tokens.id +')', - populate: function( id ){ - this.ids.push( cleanMetaChars(id) ); - } - }, - - { - name: 'className', - query: true, - regex: '\\.('+ tokens.className +')', - populate: function( className ){ - this.classes.push( cleanMetaChars(className) ); - } - }, - - { - name: 'dataExists', - query: true, - regex: '\\[\\s*('+ tokens.variable +')\\s*\\]', - populate: function( variable ){ - this.data.push({ - field: cleanMetaChars(variable) - }); - } - }, - - { - name: 'dataCompare', - query: true, - regex: '\\[\\s*('+ tokens.variable +')\\s*('+ tokens.comparatorOp +')\\s*('+ tokens.value +')\\s*\\]', - populate: function( variable, comparatorOp, value ){ - var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null; - - if( valueIsString ){ - value = value.substring(1, value.length - 1); - } else { - value = parseFloat(value); - } - - this.data.push({ - field: cleanMetaChars(variable), - operator: comparatorOp, - value: value - }); - } - }, - - { - name: 'dataBool', - query: true, - regex: '\\[\\s*('+ tokens.boolOp +')\\s*('+ tokens.variable +')\\s*\\]', - populate: function( boolOp, variable ){ - this.data.push({ - field: cleanMetaChars(variable), - operator: boolOp - }); - } - }, - - { - name: 'metaCompare', - query: true, - regex: '\\[\\[\\s*('+ tokens.meta +')\\s*('+ tokens.comparatorOp +')\\s*('+ tokens.number +')\\s*\\]\\]', - populate: function( meta, comparatorOp, number ){ - this.meta.push({ - field: cleanMetaChars(meta), - operator: comparatorOp, - value: parseFloat(number) - }); - } - }, - - { - name: 'nextQuery', - separator: true, - regex: tokens.separator, - populate: function(){ - // go on to next query - self[++i] = newQuery(); - currentSubject = null; - } - }, - - { - name: 'child', - separator: true, - regex: tokens.child, - populate: function(){ - // this query is the parent of the following query - var childQuery = newQuery(); - childQuery.parent = this; - childQuery.subject = currentSubject; - - // we're now populating the child query with expressions that follow - self[i] = childQuery; - } - }, - - { - name: 'descendant', - separator: true, - regex: tokens.descendant, - populate: function(){ - // this query is the ancestor of the following query - var descendantQuery = newQuery(); - descendantQuery.ancestor = this; - descendantQuery.subject = currentSubject; - - // we're now populating the descendant query with expressions that follow - self[i] = descendantQuery; - } - }, - - { - name: 'subject', - modifier: true, - regex: tokens.subject, - populate: function(){ - if( currentSubject != null && this.subject != this ){ - util.error('Redefinition of subject in selector `' + selector + '`'); - return false; - } - - currentSubject = this; - this.subject = this; - } - - } - ]; - - self._private.selectorText = selector; - var remaining = selector; - var i = 0; - - // of all the expressions, find the first match in the remaining text - var consumeExpr = function( expectation ){ - var expr; - var match; - var name; - - for( var j = 0; j < exprs.length; j++ ){ - var e = exprs[j]; - var n = e.name; - - // ignore this expression if it doesn't meet the expectation function - if( is.fn( expectation ) && !expectation(n, e) ){ continue; } - - var m = remaining.match(new RegExp( '^' + e.regex )); - - if( m != null ){ - match = m; - expr = e; - name = n; - - var consumed = m[0]; - remaining = remaining.substring( consumed.length ); - - break; // we've consumed one expr, so we can return now - } - } - - return { - expr: expr, - match: match, - name: name - }; - }; - - // consume all leading whitespace - var consumeWhitespace = function(){ - var match = remaining.match(/^\s+/); - - if( match ){ - var consumed = match[0]; - remaining = remaining.substring( consumed.length ); - } - }; - - self[0] = newQuery(); // get started - - consumeWhitespace(); // get rid of leading whitespace - for(;;){ - var check = consumeExpr(); - - if( check.expr == null ){ - util.error('The selector `'+ selector +'`is invalid'); - return; - } else { - var args = []; - for(var j = 1; j < check.match.length; j++){ - args.push( check.match[j] ); - } - - // let the token populate the selector object (i.e. in self[i]) - var ret = check.expr.populate.apply( self[i], args ); - - if( ret === false ){ return; } // exit if population failed - } - - // we're done when there's nothing left to parse - if( remaining.match(/^\s*$/) ){ - break; - } - } - - self.length = i + 1; - - // adjust references for subject - for(var j = 0; j < self.length; j++){ - var query = self[j]; - - 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( query.parent != null ){ // swap parent/child reference - var parent = query.parent; - var child = query; - - child.parent = null; - parent.child = child; - - query = parent; // go up the tree - } else if( query.ancestor != null ){ // swap ancestor/descendant - var ancestor = query.ancestor; - var descendant = query; - - descendant.ancestor = null; - ancestor.descendant = descendant; - - query = ancestor; // go up the tree - } else { - util.error('When adjusting references for the selector `'+ query +'`, neither parent nor ancestor was found'); - break; - } - } // for - - self[j] = query.subject; // subject should be the root query - } // if - } // for - - // make sure for each query that the subject group matches the implicit group if any - if( onlyThisGroup != null ){ - for(var j = 0; j < self.length; j++){ - if( self[j].group != null && self[j].group != onlyThisGroup ){ - util.error('Group `'+ self[j].group +'` conflicts with implicit group `'+ onlyThisGroup +'` in selector `'+ selector +'`'); - return; - } - - self[j].group = onlyThisGroup; // set to implicit group - } - } - - } else { - util.error('A selector must be created from a string; found ' + selector); - return; - } - - self._private.invalid = false; - -}; - -var selfn = Selector.prototype; - -selfn.size = function(){ - return this.length; -}; - -selfn.eq = function(i){ - return this[i]; -}; - -var queryMatches = function(query, element){ - // check group - if( query.group != null && query.group != '*' && query.group != element._private.group ){ - return false; - } - - var cy = element.cy(); - - // check colon selectors - var allColonSelectorsMatch = true; - for(var k = 0; k < query.colonSelectors.length; k++){ - var sel = query.colonSelectors[k]; - - switch(sel){ - case ':selected': - allColonSelectorsMatch = element.selected(); - break; - case ':unselected': - allColonSelectorsMatch = !element.selected(); - break; - case ':selectable': - allColonSelectorsMatch = element.selectable(); - break; - case ':unselectable': - allColonSelectorsMatch = !element.selectable(); - break; - case ':locked': - allColonSelectorsMatch = element.locked(); - break; - case ':unlocked': - allColonSelectorsMatch = !element.locked(); - break; - case ':visible': - allColonSelectorsMatch = element.visible(); - break; - case ':hidden': - allColonSelectorsMatch = !element.visible(); - break; - case ':transparent': - allColonSelectorsMatch = element.transparent(); - break; - case ':grabbed': - allColonSelectorsMatch = element.grabbed(); - break; - case ':free': - allColonSelectorsMatch = !element.grabbed(); - break; - case ':removed': - allColonSelectorsMatch = element.removed(); - break; - case ':inside': - allColonSelectorsMatch = !element.removed(); - break; - case ':grabbable': - allColonSelectorsMatch = element.grabbable(); - break; - case ':ungrabbable': - allColonSelectorsMatch = !element.grabbable(); - break; - case ':animated': - allColonSelectorsMatch = element.animated(); - break; - case ':unanimated': - allColonSelectorsMatch = !element.animated(); - break; - case ':parent': - allColonSelectorsMatch = element.isNode() && element.children().nonempty(); - break; - case ':child': - case ':nonorphan': - allColonSelectorsMatch = element.isNode() && element.parent().nonempty(); - break; - case ':orphan': - allColonSelectorsMatch = element.isNode() && element.parent().empty(); - break; - case ':loop': - allColonSelectorsMatch = element.isEdge() && element.data('source') === element.data('target'); - break; - case ':simple': - allColonSelectorsMatch = element.isEdge() && element.data('source') !== element.data('target'); - break; - case ':active': - allColonSelectorsMatch = element.active(); - break; - case ':inactive': - allColonSelectorsMatch = !element.active(); - break; - case ':touch': - allColonSelectorsMatch = is.touch(); - break; - case ':backgrounding': - allColonSelectorsMatch = element.backgrounding(); - break; - case ':nonbackgrounding': - allColonSelectorsMatch = !element.backgrounding(); - break; - } - - if( !allColonSelectorsMatch ) break; - } - if( !allColonSelectorsMatch ) return false; - - // check id - var allIdsMatch = true; - for(var k = 0; k < query.ids.length; k++){ - var id = query.ids[k]; - var actualId = element._private.data.id; - - allIdsMatch = allIdsMatch && (id == actualId); - - if( !allIdsMatch ) break; - } - if( !allIdsMatch ) return false; - - // check classes - var allClassesMatch = true; - for(var k = 0; k < query.classes.length; k++){ - var cls = query.classes[k]; - - allClassesMatch = allClassesMatch && element.hasClass(cls); - - if( !allClassesMatch ) break; - } - if( !allClassesMatch ) return false; - - // generic checking for data/metadata - var operandsMatch = function(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; - - if( operator != null && value != null ){ - - var fieldVal = params.fieldValue(field); - var fieldStr = !is.string(fieldVal) && !is.number(fieldVal) ? '' : '' + fieldVal; - var valStr = '' + value; - - var caseInsensitive = false; - if( operator.indexOf('@') >= 0 ){ - fieldStr = fieldStr.toLowerCase(); - valStr = valStr.toLowerCase(); - - operator = operator.replace('@', ''); - caseInsensitive = true; - } - - var notExpr = false; - var handledNotExpr = false; - if( operator.indexOf('!') >= 0 ){ - operator = operator.replace('!', ''); - notExpr = true; - } - - // 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(); - } - - switch(operator){ - case '*=': - matches = fieldStr.search(valStr) >= 0; - break; - case '$=': - matches = new RegExp(valStr + '$').exec(fieldStr) != null; - break; - case '^=': - matches = new RegExp('^' + valStr).exec(fieldStr) != null; - break; - case '=': - matches = fieldVal === value; - break; - case '!=': - matches = fieldVal !== value; - break; - case '>': - matches = !notExpr ? fieldVal > value : fieldVal <= value; - handledNotExpr = true; - break; - case '>=': - matches = !notExpr ? fieldVal >= value : fieldVal < value; - handledNotExpr = true; - break; - case '<': - matches = !notExpr ? fieldVal < value : fieldVal >= value; - handledNotExpr = true; - break; - case '<=': - matches = !notExpr ? fieldVal <= value : fieldVal > value; - handledNotExpr = true; - break; - default: - matches = false; - break; - - } - } else if( operator != null ){ - switch(operator){ - case '?': - matches = params.fieldTruthy(field); - break; - case '!': - matches = !params.fieldTruthy(field); - break; - case '^': - matches = params.fieldUndefined(field); - break; - } - } else { - matches = !params.fieldUndefined(field); - } - - if( notExpr && !handledNotExpr ){ - matches = !matches; - handledNotExpr = true; - } - - if( !matches ){ - allDataMatches = false; - break; - } - } // for - - return allDataMatches; - }; // operandsMatch - - // check data matches - var allDataMatches = operandsMatch({ - name: 'data', - fieldValue: function(field){ - return element._private.data[field]; - }, - fieldRef: function(field){ - return 'element._private.data.' + field; - }, - fieldUndefined: function(field){ - return element._private.data[field] === undefined; - }, - fieldTruthy: function(field){ - if( element._private.data[field] ){ - return true; - } - return false; - } - }); - - if( !allDataMatches ){ - return false; - } - - // check metadata matches - var allMetaMatches = operandsMatch({ - name: 'meta', - fieldValue: function(field){ - return element[field](); - }, - fieldRef: function(field){ - return 'element.' + field + '()'; - }, - fieldUndefined: function(field){ - return element[field]() == null; - }, - fieldTruthy: function(field){ - if( element[field]() ){ - return true; - } - return false; - } - }); - - if( !allMetaMatches ){ - return false; - } - - // check collection - if( query.collection != null ){ - var matchesAny = query.collection._private.ids[ element.id() ] != null; - - if( !matchesAny ){ - return false; - } - } - - // check filter function - if( query.filter != null && element.collection().filter( query.filter ).size() === 0 ){ - return false; - } - - - // check parent/child relations - var confirmRelations = function( query, elements ){ - if( query != null ){ - var matches = false; - - if( !cy.hasCompoundNodes() ){ - return false; - } - - elements = elements(); // make elements functional so we save cycles if query == null - - // query must match for at least one element (may be recursive) - for(var i = 0; i < elements.length; i++){ - if( queryMatches( query, elements[i] ) ){ - matches = true; - break; - } - } - - return matches; - } else { - return true; - } - }; - - if (! confirmRelations(query.parent, function(){ - return element.parent(); - }) ){ return false; } - - if (! confirmRelations(query.ancestor, function(){ - return element.parents(); - }) ){ return false; } - - if (! confirmRelations(query.child, function(){ - return element.children(); - }) ){ return false; } - - if (! confirmRelations(query.descendant, function(){ - return element.descendants(); - }) ){ return false; } - - // we've reached the end, so we've matched everything for this query - return true; -}; // queryMatches - -// filter an existing collection -selfn.filter = function(collection){ - var self = this; - var cy = collection.cy(); - - // don't bother trying if it's invalid - if( self._private.invalid ){ - return cy.collection(); - } - - var selectorFunction = function(i, element){ - for(var j = 0; j < self.length; j++){ - var query = self[j]; - - if( queryMatches(query, element) ){ - return true; - } - } - - return false; - }; - - if( self._private.selectorText == null ){ - selectorFunction = function(){ return true; }; - } - - var filteredCollection = collection.filter( selectorFunction ); - - return filteredCollection; -}; // filter - -// does selector match a single element? -selfn.matches = function(ele){ - var self = this; - - // don't bother trying if it's invalid - if( self._private.invalid ){ - return false; - } - - for(var j = 0; j < self.length; j++){ - var query = self[j]; - - if( queryMatches(query, ele) ){ - return true; - } - } - - return false; -}; // filter - -// ith query to string -selfn.toString = selfn.selector = function(){ - - var str = ''; - - var clean = function(obj, isValue){ - if( is.string(obj) ){ - return isValue ? '"' + obj + '"' : obj; - } - return ''; - }; - - var queryToString = function(query){ - var str = ''; - - if( query.subject === query ){ - str += '$'; - } - - var group = clean(query.group); - str += group.substring(0, group.length - 1); - - for(var j = 0; j < query.data.length; j++){ - var data = query.data[j]; - - if( data.value ){ - str += '[' + data.field + clean(data.operator) + clean(data.value, true) + ']'; - } else { - str += '[' + clean(data.operator) + data.field + ']'; - } - } - - for(var j = 0; j < query.meta.length; j++){ - var meta = query.meta[j]; - str += '[[' + meta.field + clean(meta.operator) + clean(meta.value, true) + ']]'; - } - - for(var j = 0; j < query.colonSelectors.length; j++){ - var sel = query.colonSelectors[i]; - str += sel; - } - - for(var j = 0; j < query.ids.length; j++){ - var sel = '#' + query.ids[i]; - str += sel; - } - - for(var j = 0; j < query.classes.length; j++){ - var sel = '.' + query.classes[j]; - str += sel; - } - - if( query.parent != null ){ - str = queryToString( query.parent ) + ' > ' + str; - } - - if( query.ancestor != null ){ - str = queryToString( query.ancestor ) + ' ' + str; - } - - if( query.child != null ){ - str += ' > ' + queryToString( query.child ); - } - - if( query.descendant != null ){ - str += ' ' + queryToString( query.descendant ); - } - - return str; - }; - - for(var i = 0; i < this.length; i++){ - var query = this[i]; - - str += queryToString( query ); - - if( this.length > 1 && i < this.length - 1 ){ - str += ', '; - } - } - - return str; -}; - -module.exports = Selector; - -},{"./is":77,"./util":94}],82:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); -var is = _dereq_('../is'); - -var styfn = {}; - -// (potentially expensive calculation) -// apply the style to the element based on -// - its bypass -// - what selectors match it -styfn.apply = function( eles ){ - var self = this; - - if( self._private.newStyle ){ // clear style caches - this._private.contextStyles = {}; - this._private.propDiffs = {}; - } - - for( var ie = 0; ie < eles.length; ie++ ){ - var ele = eles[ie]; - var cxtMeta = self.getContextMeta( ele ); - var cxtStyle = self.getContextStyle( cxtMeta ); - var app = self.applyContextStyle( cxtMeta, cxtStyle, ele ); - - self.updateTransitions( ele, app.diffProps ); - self.updateStyleHints( ele ); - - } // for elements - - self._private.newStyle = false; -}; - -styfn.getPropertiesDiff = function( oldCxtKey, newCxtKey ){ - var self = this; - var cache = self._private.propDiffs = self._private.propDiffs || {}; - var dualCxtKey = oldCxtKey + '-' + newCxtKey; - var cachedVal = cache[dualCxtKey]; - - if( cachedVal ){ - return cachedVal; - } - - var diffProps = []; - var addedProp = {}; - - 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; - - if( cxtHasDiffed || cxtHasMappedProps ){ - var props; - - 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 - } - - for( var j = 0; j < props.length; j++ ){ - var prop = props[j]; - var name = prop.name; - - // 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'; - - if( !hasLaterCxt ){ continue; } // can't override unless the context is active - - laterCxtOverrides = laterCxt.properties[ prop.name ] != null; - - if( laterCxtOverrides ){ break; } // exit early as long as one later context overrides - } - - if( !addedProp[name] && !laterCxtOverrides ){ - addedProp[name] = true; - diffProps.push( name ); - } - } // for props - } // if - - } // for contexts - - cache[ dualCxtKey ] = diffProps; - return diffProps; -}; - -styfn.getContextMeta = function( ele ){ - var self = this; - var cxtKey = ''; - var diffProps; - var prevKey = ele._private.styleCxtKey || ''; - - if( self._private.newStyle ){ - prevKey = ''; // since we need to apply all style if a fresh stylesheet - } - - // 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( contextSelectorMatches ){ - cxtKey += 't'; - } else { - cxtKey += 'f'; - } - } // for context - - diffProps = self.getPropertiesDiff( prevKey, cxtKey ); - - ele._private.styleCxtKey = cxtKey; - - return { - key: cxtKey, - diffPropNames: diffProps - }; -}; - -// 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 || {}; - - // if already computed style, returned cached copy - if( cxtStyles[cxtKey] ){ return cxtStyles[cxtKey]; } - - var style = { - _private: { - key: cxtKey - } - }; - - for( var i = 0; i < self.length; i++ ){ - var cxt = self[i]; - var hasCxt = cxtKey[i] === 't'; - - if( !hasCxt ){ continue; } - - for( var j = 0; j < cxt.properties.length; j++ ){ - var prop = cxt.properties[j]; - var styProp = style[ prop.name ] = prop; - - styProp.context = cxt; - } - } - - cxtStyles[cxtKey] = style; - return style; -}; - -styfn.applyContextStyle = function( cxtMeta, cxtStyle, ele ){ - var self = this; - var diffProps = cxtMeta.diffPropNames; - var retDiffProps = {}; - - for( var i = 0; i < diffProps.length; i++ ){ - var diffPropName = diffProps[i]; - var cxtProp = cxtStyle[ diffPropName ]; - var eleProp = ele._private.style[ diffPropName ]; - - // save cycles when the context prop doesn't need to be applied - if( !cxtProp || eleProp === cxtProp ){ continue; } - - var retDiffProp = retDiffProps[ diffPropName ] = { - prev: eleProp - }; - - self.applyParsedProperty( ele, cxtProp ); - - retDiffProp.next = ele._private.style[ diffPropName ]; - - if( retDiffProp.next && retDiffProp.next.bypass ){ - retDiffProp.next = retDiffProp.next.bypassed; - } - } - - return { - diffProps: retDiffProps - }; -}; - -styfn.updateStyleHints = function(ele){ - var _p = ele._private; - var self = this; - var style = _p.style; - - if( ele.removed() ){ return; } - - // set whether has pie or not; for greater efficiency - var hasPie = false; - if( _p.group === 'nodes' && self._private.hasPie ){ - for( var i = 1; i <= self.pieBackgroundN; i++ ){ // 1..N - var size = _p.style['pie-' + i + '-background-size'].value; - - if( size > 0 ){ - hasPie = true; - break; - } - } - } - - _p.hasPie = hasPie; - - var transform = style['text-transform'].strValue; - var content = style['label'].strValue; - var fStyle = style['font-style'].strValue; - var size = style['font-size'].pfValue + 'px'; - var family = style['font-family'].strValue; - // var variant = style['font-variant'].strValue; - var weight = style['font-weight'].strValue; - var valign = style['text-valign'].strValue; - var halign = style['text-valign'].strValue; - var oWidth = style['text-outline-width'].pfValue; - var wrap = style['text-wrap'].strValue; - var wrapW = style['text-max-width'].pfValue; - _p.labelKey = fStyle +'$'+ size +'$'+ family +'$'+ weight +'$'+ content +'$'+ transform +'$'+ valign +'$'+ halign +'$'+ oWidth + '$' + wrap + '$' + wrapW; - _p.fontKey = fStyle +'$'+ weight +'$'+ size +'$'+ family; - - var width = style['width'].pfValue; - var height = style['height'].pfValue; - var borderW = style['border-width'].pfValue; - _p.boundingBoxKey = width +'$'+ height +'$'+ borderW; - - if( ele._private.group === 'edges' ){ - var cpss = style['control-point-step-size'].pfValue; - var cpd = style['control-point-distances'] ? style['control-point-distances'].pfValue.join('_') : undefined; - var cpw = style['control-point-weights'].value.join('_'); - var curve = style['curve-style'].strValue; - - _p.boundingBoxKey += '$'+ cpss +'$'+ cpd +'$'+ cpw +'$'+ curve; - } - - _p.styleKey = Date.now(); -}; - -// 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, flatProp; - 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; - - // can't apply auto to width or height unless it's a parent node - if( (parsedProp.name === 'height' || parsedProp.name === 'width') && ele.isNode() ){ - if( parsedProp.value === 'auto' && !ele.isParent() ){ - return false; - } else if( parsedProp.value !== 'auto' && ele.isParent() ){ - prop = parsedProp = this.parse( parsedProp.name, 'auto', propIsBypass ); - } - } - - // check if we need to delete the current bypass - if( propIsBypass && prop.deleteBypass ){ // then this property is just here to indicate we need to delete - var currentProp = style[ prop.name ]; - - // can only delete if the current prop is a bypass and it points to the property it was overriding - if( !currentProp ){ - return true; // property is already not defined - } else if( currentProp.bypass && currentProp.bypassed ){ // 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 ] = currentProp.bypassed; - return true; - - } else { - return false; // we're unsuccessful deleting the bypass - } - } - - var printMappingErr = function(){ - 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'); - }; - - // put the property in the style objects - switch( prop.mapped ){ // flatten the property if mapped - case types.mapData: - case types.mapLayoutData: - case types.mapScratch: - - var isLayout = prop.mapped === types.mapLayoutData; - var isScratch = prop.mapped === types.mapScratch; - - // flatten the field (e.g. data.foo.bar) - var fields = prop.field.split("."); - var fieldVal; - - if( isScratch || isLayout ){ - fieldVal = _p.scratch; - } else { - fieldVal = _p.data; - } - - for( var i = 0; i < fields.length && fieldVal; i++ ){ - var field = fields[i]; - fieldVal = fieldVal[ field ]; - } - - var percent; - if( !is.number(fieldVal) ){ // then keep the mapping but assume 0% for now - percent = 0; - } else { - percent = (fieldVal - prop.fieldMin) / (prop.fieldMax - prop.fieldMin); - } - - // make sure to bound percent value - if( percent < 0 ){ - percent = 0; - } else if( percent > 1 ){ - percent = 1; - } - - 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]; - - 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 ) - ]; - - 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, true ); - - } else { - return false; // can only map to colours and numbers - } - - 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, true ); - } - - if( !flatProp ){ printMappingErr(); } - flatProp.mapping = prop; // keep a reference to the mapping - prop = flatProp; // the flattened (mapped) property is the one we want - - break; - - // direct mapping - case types.data: - case types.layoutData: - case types.scratch: - var isLayout = prop.mapped === types.layoutData; - var isScratch = prop.mapped === types.scratch; - - // flatten the field (e.g. data.foo.bar) - var fields = prop.field.split("."); - var fieldVal; - - if( isScratch || isLayout ){ - fieldVal = _p.scratch; - } else { - fieldVal = _p.data; - } - - if( fieldVal ){ for( var i = 0; i < fields.length; i++ ){ - var field = fields[i]; - fieldVal = fieldVal[ field ]; - } } - - flatProp = this.parse( prop.name, fieldVal, prop.bypass, true ); - - 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 : ''; - - flatProp = this.parse( prop.name, flatPropVal, prop.bypass, true ); - } - - if( !flatProp ){ printMappingErr(); } - flatProp.mapping = prop; // keep a reference to the mapping - prop = flatProp; // the flattened (mapped) property is the one we want - - break; - - case types.fn: - var fn = prop.value; - var fnRetVal = fn( ele ); - - flatProp = this.parse( prop.name, fnRetVal, prop.bypass, true ); - flatProp.mapping = prop; // keep a reference to the mapping - prop = flatProp; // the flattened (mapped) property is the one we want - - break; - - case undefined: - break; // just set the property - - default: - return false; // not a valid mapping - } - - // 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; - } - - 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; - } - } - - return true; -}; - -// 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.elements(); - - eles.updateStyle(); -}; - -// just update the functional properties (i.e. mappings) in the elements' -// styles (less expensive than recalculation) -styfn.updateMappers = function( eles ){ - var self = this; - - for( var i = 0; i < eles.length; i++ ){ // for each ele - var ele = eles[i]; - var style = ele._private.style; - - for( var j = 0; j < self.properties.length; j++ ){ // for each prop - var prop = self.properties[j]; - var propInStyle = style[ prop.name ]; - - if( propInStyle && propInStyle.mapping ){ - var mapping = propInStyle.mapping; - this.applyParsedProperty( ele, mapping ); // reapply the mapping property - } - } - - this.updateStyleHints( ele ); - } -}; - -// diffProps : { name => { prev, next } } -styfn.updateTransitions = function( ele, diffProps, isBypass ){ - var self = this; - var _p = ele._private; - var style = _p.style; - var props = style['transition-property'].value; - var duration = style['transition-duration'].pfValue; - var delay = style['transition-delay'].pfValue; - var css = {}; - - if( props.length > 0 && duration > 0 ){ - - // build up the style to animate towards - var anyPrev = false; - for( var i = 0; i < props.length; i++ ){ - var prop = props[i]; - var styProp = style[ prop ]; - var diffProp = diffProps[ prop ]; - - if( !diffProp ){ continue; } - - var prevProp = diffProp.prev; - var fromProp = prevProp; - var toProp = diffProp.next != null ? diffProp.next : styProp; - var diff = false; - var initVal; - var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity) - - if( !fromProp ){ continue; } - - // 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; - - // 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; - - // 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] - ; - - initVal = fromProp.strValue; - } - - // the previous value is good for an animation only if it's different - if( diff ){ - css[ prop ] = toProp.strValue; // to val - this.applyBypass( ele, prop, initVal ); // from val - anyPrev = true; - } - - } // end if props allow ani - - // can't transition if there's nothing previous to transition from - if( !anyPrev ){ return; } - - _p.transitioning = true; - - ele.stop(); - - if( delay > 0 ){ - ele.delay( delay ); - } - - ele.animate({ - css: css - }, { - duration: duration, - easing: style['transition-timing-function'].value, - queue: false, - complete: function(){ - if( !isBypass ){ - self.removeBypasses( ele, props ); - } - - _p.transitioning = false; - } - }); - - } else if( _p.transitioning ){ - ele.stop(); - - this.removeBypasses( ele, props ); - - _p.transitioning = false; - } -}; - -module.exports = styfn; - -},{"../is":77,"../util":94}],83:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var util = _dereq_('../util'); - -var styfn = {}; - -// 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; - - // put all the properties (can specify one or many) in an array after parsing them - if( name === "*" || name === "**" ){ // apply to all property names - - if( value !== undefined ){ - for( var i = 0; i < self.properties.length; i++ ){ - var prop = self.properties[i]; - var name = prop.name; - - var parsedProp = this.parse(name, value, true); - - if( parsedProp ){ - props.push( parsedProp ); - } - } - } - - } else if( is.string(name) ){ // then parse the single property - var parsedProp = this.parse(name, value, true); - - if( parsedProp ){ - props.push( parsedProp ); - } - } else if( is.plainObject(name) ){ // then parse each property - var specifiedProps = name; - updateTransitions = value; - - for( var i = 0; i < self.properties.length; i++ ){ - var prop = self.properties[i]; - var name = prop.name; - var value = specifiedProps[ name ]; - - if( value === undefined ){ // try camel case name too - value = specifiedProps[ util.dash2camel(name) ]; - } - - if( value !== undefined ){ - var parsedProp = this.parse(name, value, true); - - if( parsedProp ){ - props.push( parsedProp ); - } - } - } - } 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; } - - // now, apply the bypass properties on the elements - var ret = false; // return true if at least one succesful bypass applied - for( var i = 0; i < eles.length; i++ ){ // for each ele - var ele = eles[i]; - var style = ele._private.style; - var diffProps = {}; - var diffProp; - - for( var j = 0; j < props.length; j++ ){ // for each prop - var prop = props[j]; - - if( updateTransitions ){ - var prevProp = style[ prop.name ]; - diffProp = diffProps[ prop.name ] = { prev: prevProp }; - } - - ret = this.applyParsedProperty( ele, prop ) || ret; - - if( updateTransitions ){ - diffProp.next = style[ prop.name ]; - } - - } // for props - - if( ret ){ - this.updateStyleHints( ele ); - } - - if( updateTransitions ){ - this.updateTransitions( ele, diffProps, isBypass ); - } - } // for eles - - return ret; -}; - -// only useful in specific cases like animation -styfn.overrideBypass = function( eles, name, value ){ - name = util.camel2dash(name); - - 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; - - if( !prop.bypass ){ // need a bypass if one doesn't exist - this.applyBypass( ele, name, value ); - continue; - } - - prop.value = value; - - if( prop.pfValue != null ){ - prop.pfValue = value; - } - - if( isColor ){ - prop.strValue = 'rgb(' + value.join(',') + ')'; - } else if( isMulti ){ - prop.strValue = value.join(' '); - } else { - prop.strValue = '' + 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 = {}; - var style = ele._private.style; - - for( var i = 0; i < props.length; i++ ){ - var name = props[i]; - var prop = this.properties[ name ]; - var value = ''; // empty => remove bypass - var parsedProp = this.parse(name, value, true); - var prevProp = style[ prop.name ]; - var diffProp = diffProps[ prop.name ] = { prev: prevProp }; - - this.applyParsedProperty(ele, parsedProp); - - diffProp.next = style[ prop.name ]; - } // for props - - this.updateStyleHints( ele ); - - if( updateTransitions ){ - this.updateTransitions( ele, diffProps, isBypass ); - } - } // for eles -}; - -module.exports = styfn; - -},{"../is":77,"../util":94}],84:[function(_dereq_,module,exports){ -'use strict'; - -var window = _dereq_('../window'); - -var styfn = {}; - -// gets what an em size corresponds to in pixels relative to a dom element -styfn.getEmSizeInPixels = function(){ - var px = this.containerCss('font-size'); - - if( px != null ){ - return parseFloat( px ); - } else { - return 1; // for headless - } -}; - -// gets css property from the core container -styfn.containerCss = function( propName ){ - var cy = this._private.cy; - var domElement = cy.container(); - - if( window && domElement && window.getComputedStyle ){ - return window.getComputedStyle(domElement).getPropertyValue( propName ); - } -}; - -module.exports = styfn; - -},{"../window":100}],85:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); -var is = _dereq_('../is'); - -var styfn = {}; - -// gets the rendered style for an element -styfn.getRenderedStyle = function( ele ){ - return this.getRawStyle( ele, true ); -}; - -// gets the raw style for an element -styfn.getRawStyle = function( ele, isRenderedVal ){ - var self = this; - var ele = ele[0]; // insure it's an element - - if( ele ){ - var rstyle = {}; - - for( var i = 0; i < self.properties.length; i++ ){ - var prop = self.properties[i]; - var val = self.getStylePropertyValue( ele, prop.name, isRenderedVal ); - - if( val ){ - rstyle[ prop.name ] = val; - rstyle[ util.dash2camel(prop.name) ] = val; - } - } - - return rstyle; - } -}; - -styfn.getStylePropertyValue = function( ele, propName, isRenderedVal ){ - var self = this; - var ele = ele[0]; // insure it's an element - - if( ele ){ - var style = ele._private.style; - var prop = self.properties[ propName ]; - var type = prop.type; - var styleProp = style[ prop.name ]; - var zoom = ele.cy().zoom(); - - 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; - - return val; - } - } -}; - -// gets the value style for an element (useful for things like animations) -styfn.getValueStyle = function( ele ){ - var self = this; - var rstyle = {}; - var style; - var isEle = is.element(ele); - - if( isEle ){ - style = ele._private.style; - } else { - style = ele; // just passed the style itself - } - - if( style ){ - for( var i = 0; i < self.properties.length; i++ ){ - var prop = self.properties[i]; - var styleProp = style[ prop.name ] || style[ util.dash2camel(prop.name) ]; - - if( styleProp !== undefined ){ // then make a prop of it - if( is.plainObject( styleProp ) ){ - styleProp = this.parse( prop.name, styleProp.strValue ); - } else { - styleProp = this.parse( prop.name, styleProp ); - } - } - - if( styleProp ){ - rstyle[ prop.name ] = styleProp; - rstyle[ util.dash2camel(prop.name) ] = styleProp; - } - } - } - - return rstyle; -}; - -styfn.getPropsList = function( propsObj ){ - var self = this; - var rstyle = []; - var style = propsObj; - var props = self.properties; - - if( style ){ - for( var name in style ){ - var val = style[name]; - var prop = props[name] || props[ util.camel2dash(name) ]; - var styleProp = this.parse( prop.name, val ); - - rstyle.push( styleProp ); - } - } - - return rstyle; -}; - -module.exports = styfn; - -},{"../is":77,"../util":94}],86:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var util = _dereq_('../util'); -var Selector = _dereq_('../selector'); - -var Style = function( 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: {}, - newStyle: true - }; - - this.length = 0; - - this.addDefaultStylesheet(); -}; - -var styfn = Style.prototype; - -styfn.instanceString = function(){ - return 'style'; -}; - -// remove all contexts -styfn.clear = function(){ - for( var i = 0; i < this.length; i++ ){ - this[i] = undefined; - } - this.length = 0; - this._private.newStyle = true; - - return this; // chaining -}; - -styfn.resetToDefault = function(){ - this.clear(); - this.addDefaultStylesheet(); - - return this; -}; - -// builds a style object for the 'core' selector -styfn.core = function(){ - return this._private.coreStyle; -}; - -// create a new context from the specified selector string and switch to that context -styfn.selector = function( selectorStr ){ - // 'core' is a special case and does not need a selector - var selector = selectorStr === 'core' ? null : new Selector( selectorStr ); - - var i = this.length++; // new context means new index - this[i] = { - selector: selector, - properties: [], - mappedProperties: [], - index: i - }; - - return this; // chaining -}; - -// add one or many css rules to the current context -styfn.css = function(){ - var self = this; - var args = arguments; - - switch( args.length ){ - case 1: - var map = args[0]; - - for( var i = 0; i < self.properties.length; i++ ){ - var prop = self.properties[i]; - var mapVal = map[ prop.name ]; - - if( mapVal === undefined ){ - mapVal = map[ util.dash2camel(prop.name) ]; - } - - if( mapVal !== undefined ){ - this.cssRule( prop.name, mapVal ); - } - } - - break; - - case 2: - this.cssRule( args[0], args[1] ); - break; - - default: - break; // do nothing if args are invalid - } - - return this; // chaining -}; -styfn.style = styfn.css; - -// add a single css rule to the current context -styfn.cssRule = function( name, value ){ - // name-value pair - var property = this.parse( name, value ); - - // add property to current context if valid - if( property ){ - var i = this.length - 1; - this[i].properties.push( property ); - this[i].properties[ property.name ] = property; // allow access by name as well - - if( property.name.match(/pie-(\d+)-background-size/) && property.value ){ - this._private.hasPie = true; - } - - if( property.mapped ){ - this[i].mappedProperties.push( property ); - } - - // add to core style if necessary - var currentSelectorIsCore = !this[i].selector; - if( currentSelectorIsCore ){ - this._private.coreStyle[ property.name ] = property; - } - } - - return this; // chaining -}; - -// static function -Style.fromJson = function( cy, json ){ - var style = new Style( cy ); - - style.fromJson( json ); - - return style; -}; - -Style.fromString = function( cy, string ){ - return new Style( cy ).fromString( string ); -}; - -[ - _dereq_('./apply'), - _dereq_('./bypass'), - _dereq_('./container'), - _dereq_('./get-for-ele'), - _dereq_('./json'), - _dereq_('./string-sheet'), - _dereq_('./properties'), - _dereq_('./parse') -].forEach(function( props ){ - util.extend( styfn, props ); -}); - - -Style.types = styfn.types; -Style.properties = styfn.properties; - -module.exports = Style; - -},{"../is":77,"../selector":81,"../util":94,"./apply":82,"./bypass":83,"./container":84,"./get-for-ele":85,"./json":87,"./parse":88,"./properties":89,"./string-sheet":90}],87:[function(_dereq_,module,exports){ -'use strict'; - -var styfn = {}; - -styfn.applyFromJson = function( json ){ - var style = this; - - for( var i = 0; i < json.length; i++ ){ - var context = json[i]; - var selector = context.selector; - var props = context.style || context.css; - - style.selector( selector ); // apply selector - - for( var name in props ){ - var value = props[name]; - - style.css( name, value ); // apply property - } - } - - return style; -}; - -// accessible cy.style() function -styfn.fromJson = function( json ){ - var style = this; - - style.resetToDefault(); - style.applyFromJson( json ); - - return style; -}; - -// get json from cy.style() api -styfn.json = function(){ - var json = []; - - for( var i = this.defaultLength; i < this.length; i++ ){ - var cxt = this[i]; - var selector = cxt.selector; - var props = cxt.properties; - var css = {}; - - for( var j = 0; j < props.length; j++ ){ - var prop = props[j]; - css[ prop.name ] = prop.strValue; - } - - json.push({ - selector: !selector ? 'core' : selector.toString(), - style: css - }); - } - - return json; -}; - -module.exports = styfn; - -},{}],88:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); -var is = _dereq_('../is'); - -var styfn = {}; - -// a caching layer for property parsing -styfn.parse = function( name, value, propIsBypass, propIsFlat ){ - var argHash = [ name, value, propIsBypass, propIsFlat ].join('$'); - var propCache = this.propCache = this.propCache || {}; - var ret; - var impl = parseImpl.bind( this ); - - if( !(ret = propCache[argHash]) ){ - ret = propCache[argHash] = impl( name, value, propIsBypass, propIsFlat ); - } - - // always need a copy since props are mutated later in their lifecycles - ret = util.copy( ret ); - - if( ret ){ - ret.value = util.copy( ret.value ); // because it could be an array, e.g. colour - } - - return ret; -}; - -// 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 -var parseImpl = function( name, value, propIsBypass, propIsFlat ){ - var self = this; - - name = util.camel2dash( name ); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName') - - 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 - - // the property may be an alias - if( property.alias ){ - property = property.pointsTo; - name = property.name; - } - - var valueIsString = is.string(value); - if( valueIsString ){ // trim the value to make parsing easier - value = value.trim(); - } - - 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 - }; - } - - // 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 - }; - } - - // check if value is mapped - var data, mapData, layoutData, mapLayoutData, scratch, mapScratch; - if( !valueIsString || propIsFlat ){ - // then don't bother to do the expensive regex checks - - } else if( - ( data = new RegExp( types.data.regex ).exec( value ) ) || - ( layoutData = new RegExp( types.layoutData.regex ).exec( value ) ) || - ( scratch = new RegExp( types.scratch.regex ).exec( value ) ) - ){ - if( propIsBypass ){ return false; } // mappers not allowed in bypass - - var mapped; - if( data ){ - mapped = types.data; - } else if( layoutData ){ - mapped = types.layoutData; - } else { - mapped = types.scratch; - } - - data = data || layoutData || scratch; - - return { - name: name, - value: data, - strValue: '' + value, - mapped: mapped, - field: data[1], - bypass: propIsBypass - }; - - } else if( - ( mapData = new RegExp( types.mapData.regex ).exec( value ) ) || - ( mapLayoutData = new RegExp( types.mapLayoutData.regex ).exec( value ) ) || - ( mapScratch = new RegExp( types.mapScratch.regex ).exec( value ) ) - ){ - if( propIsBypass ){ return false; } // mappers not allowed in bypass - if( type.multiple ){ return false; } // impossible to map to num - - var mapped; - if( mapData ){ - mapped = types.mapData; - } else if( mapLayoutData ){ - mapped = types.mapLayoutData; - } else { - mapped = types.mapScratch; - } - - mapData = mapData || mapLayoutData || mapScratch; - - // we can map only if the type is a colour or a number - if( !(type.color || type.number) ){ return false; } - - var valueMin = this.parse( name, mapData[4] ); // parse to validate - if( !valueMin || valueMin.mapped ){ return false; } // can't be invalid or mapped - - var valueMax = this.parse( name, mapData[5] ); // parse to validate - if( !valueMax || valueMax.mapped ){ return false; } // can't be invalid or mapped - - // 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 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? - ) - ) - ; - - if( same ){ return false; } // can't make a mapper without a range - } - - 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( type.multiple && !propIsFlat ){ - var vals; - - if( valueIsString ){ - vals = value.split(/\s+/); - } else if( is.array(value) ){ - vals = value; - } else { - vals = [ value ]; - } - - if( type.evenMultiple && vals.length % 2 !== 0 ){ return null; } - - var valArr = vals.map(function( v ){ - var p = self.parse( name, v, propIsBypass, true ); - - if( p.pfValue != null ){ - return p.pfValue; - } else { - return p.value; - } - }); - - return { - name: name, - value: valArr, - pfValue: valArr, - strValue: valArr.join(' '), - bypass: propIsBypass, - units: type.number && !type.unitless ? type.implicitUnits || 'px' : undefined - }; - } - - // several types also allow enums - var checkEnums = function(){ - 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 - }; - } - } - - return null; - }; - - // check the type and return the appropriate object - if( type.number ){ - var units; - var implicitUnits = 'px'; // not set => px - - 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 - } - } - - value = parseFloat( value ); - - // if not a number and enums not allowed, then the value is invalid - if( isNaN(value) && type.enums === undefined ){ - return null; - } - - // 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; - - return checkEnums(); - } - - // check if value must be an integer - if( type.integer && !is.integer(value) ){ - return null; - } - - // check value is within range - if( (type.min !== undefined && value < type.min) - || (type.max !== undefined && value > type.max) - ){ - return null; - } - - var ret = { - name: name, - value: value, - strValue: '' + value + (units ? units : ''), - units: units, - bypass: propIsBypass - }; - - // normalise value in pixels - if( type.unitless || (units !== 'px' && units !== 'em') ){ - ret.pfValue = value; - } else { - ret.pfValue = ( units === 'px' || !units ? (value) : (this.getEmSizeInPixels() * value) ); - } - - // 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 : value * Math.PI/180; - } - - return ret; - - } else if( type.propList ) { - - var props = []; - var propsStr = '' + value; - - if( propsStr === 'none' ){ - // leave empty - - } else { // go over each prop - - var propsSplit = propsStr.split(','); - for( var i = 0; i < propsSplit.length; i++ ){ - var propName = propsSplit[i].trim(); - - if( self.properties[propName] ){ - props.push( propName ); - } - } - - if( props.length === 0 ){ return null; } - } - - 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; } - - return { - name: name, - value: tuple, - strValue: '' + value, - bypass: propIsBypass, - roundValue: true - }; - - } else if( type.regex || type.regexes ){ - - // first check enums - if( type.enums ){ - var enumProp = checkEnums(); - - if( enumProp ){ return enumProp; } - } - - var regexes = type.regexes ? type.regexes : [ type.regex ]; - - for( var i = 0; i < regexes.length; i++ ){ - var regex = new RegExp( regexes[i] ); // make a regex from the type string - var m = regex.exec( value ); - - if( m ){ // regex matches - return { - name: name, - value: m, - strValue: '' + value, - bypass: propIsBypass - }; - - } - } - - 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 - } - -}; - -module.exports = styfn; - -},{"../is":77,"../util":94}],89:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); - -var styfn = {}; - -(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( prefix ){ return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$'; }; - var mapData = function( 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 + ')\\)$'; - }; - - // 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 }, - 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: ['auto', 'label'] }, - number: { number: true, unitless: true }, - numbers: { number: true, unitless: true, multiple: true }, - size: { number: true, min: 0 }, - bidirectionalSize: { number: true }, // allows negative - bidirectionalSizes: { number: true, multiple: true }, // allows negative - bgSize: { number: true, min: 0, allowPercent: true }, - bgWH: { number: true, min: 0, allowPercent: true, enums: ['auto'] }, - bgPos: { number: true, allowPercent: true }, - bgRepeat: { enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'] }, - bgFit: { enums: ['none', 'contain', 'cover'] }, - 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- \\"]+)*)$' }, - fontVariant: { 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'] }, - textBackgroundShape: { enums: ['rectangle', 'roundrectangle']}, - nodeShape: { enums: ['rectangle', 'roundrectangle', 'ellipse', 'triangle', 'square', 'pentagon', 'hexagon', 'heptagon', 'octagon', 'star', 'diamond', 'vee', 'rhomboid', 'polygon'] }, - compoundIncludeLabels: { enums: ['include', 'exclude'] }, - arrowShape: { enums: ['tee', 'triangle', 'triangle-tee', 'triangle-backcurve', 'half-triangle-overshot', 'vee', 'square', 'circle', 'diamond', 'none'] }, - arrowFill: { enums: ['filled', 'hollow'] }, - display: { enums: ['element', 'none'] }, - visibility: { enums: ['hidden', 'visible'] }, - 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: { regex: '^url\\s*\\(\\s*([^\\s]+)\\s*\\s*\\)|none|(.+)$' }, - propList: { propList: true }, - angle: { number: true, units: 'deg|rad', implicitUnits: 'rad' }, - textRotation: { enums: ['none', 'autorotate'] }, - polygonPointList: { number: true, multiple: true, evenMultiple: true, min: -1, max: 1, unitless: true }, - 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' - ] - } - }; - - // define visual style properties - var t = styfn.types; - var props = styfn.properties = [ - // labels - { name: 'text-valign', type: t.valign }, - { name: 'text-halign', type: t.halign }, - { name: 'color', type: t.color }, - { name: 'label', type: t.text }, - { 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-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: 'text-rotation', type: t.angle }, // TODO disabled b/c rotation breaks bounding boxes - { name: 'font-family', type: t.fontFamily }, - { name: 'font-style', type: t.fontStyle }, - // { name: 'font-variant', type: t.fontVariant }, // not useful - { name: 'font-weight', type: t.fontWeight }, - { name: 'font-size', type: t.size }, - { name: 'min-zoomed-font-size', type: t.size }, - { name: 'edge-text-rotation', type: t.textRotation }, - - // behaviour - { name: 'events', type: t.bool }, - - // visibility - { name: 'display', type: t.display }, - { name: 'visibility', type: t.visibility }, - { name: 'opacity', type: t.zeroOneNumber }, - { name: 'z-index', type: t.nonNegativeInt }, - - // overlays - { name: 'overlay-padding', type: t.size }, - { name: 'overlay-color', type: t.color }, - { name: 'overlay-opacity', type: t.zeroOneNumber }, - - // shadows - { name: 'shadow-blur', type: t.size }, - { name: 'shadow-color', type: t.color }, - { name: 'shadow-opacity', type: t.zeroOneNumber }, - { name: 'shadow-offset-x', type: t.bidirectionalSize }, - { name: 'shadow-offset-y', type: t.bidirectionalSize }, - - // label shadows - { name: 'text-shadow-blur', type: t.size }, - { name: 'text-shadow-color', type: t.color }, - { name: 'text-shadow-opacity', type: t.zeroOneNumber }, - { name: 'text-shadow-offset-x', type: t.bidirectionalSize }, - { name: 'text-shadow-offset-y', type: t.bidirectionalSize }, - - // 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 }, - - // 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-left', type: t.size }, - { name: 'padding-right', type: t.size }, - { name: 'padding-top', type: t.size }, - { name: 'padding-bottom', type: t.size }, - - // 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 }, - - // node background images - { name: 'background-image', type: t.url }, - { name: 'background-image-opacity', type: t.zeroOneNumber }, - { name: 'background-position-x', type: t.bgPos }, - { name: 'background-position-y', type: t.bgPos }, - { 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 }, - - // compound props - { name: 'position', type: t.position }, - { name: 'compound-sizing-wrt-labels', type: t.compoundIncludeLabels }, - - // 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: '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 }, - - // 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 } - ]; - - // 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' } - ]; - - // 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.bgSize }); - 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 }); - } - - // 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; - - props.push({ name: name, type: type }); - }); - }, {}); - - // list of property names - styfn.propertyNames = props.map(function(p){ return p.name; }); - - // allow access of properties by name ( e.g. style.properties.height ) - for( var i = 0; i < props.length; i++ ){ - var prop = props[i]; - - props[ prop.name ] = prop; // allow lookup by name - } - - // map aliases - for( var i = 0; i < aliases.length; i++ ){ - var alias = aliases[i]; - var pointsToProp = props[ alias.pointsTo ]; - var aliasProp = { - name: alias.name, - alias: true, - pointsTo: pointsToProp - }; - - // add alias prop for parsing - props.push( aliasProp ); - - props[ alias.name ] = aliasProp; // allow lookup by name - } -})(); - -// adds the default stylesheet to the current style -styfn.addDefaultStylesheet = function(){ - // fill the style with the default stylesheet - this - .selector('node, edge') // common properties - .css( util.extend( { - '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-border-opacity': 0, - 'text-border-width': 0, - 'text-border-style': 'solid', - 'text-border-color':'#000', - 'text-background-shape':'rectangle', - 'font-family': 'Helvetica Neue, Helvetica, sans-serif', - 'font-style': 'normal', - // 'font-variant': fontVariant, - 'font-weight': 'normal', - 'font-size': 16, - 'min-zoomed-font-size': 0, - 'edge-text-rotation': 'none', - 'visibility': 'visible', - 'display': 'element', - 'opacity': 1, - 'z-index': 0, - 'label': '', - 'overlay-opacity': 0, - 'overlay-color': '#000', - 'overlay-padding': 10, - 'shadow-opacity': 0, - 'shadow-color': '#000', - 'shadow-blur': 10, - 'shadow-offset-x': 0, - 'shadow-offset-y': 0, - 'text-shadow-opacity': 0, - 'text-shadow-color': '#000', - 'text-shadow-blur': 5, - 'text-shadow-offset-x': 0, - 'text-shadow-offset-y': 0, - 'transition-property': 'none', - 'transition-duration': 0, - 'transition-delay': 0, - 'transition-timing-function': 'linear', - - // node props - 'background-blacken': 0, - 'background-color': '#888', - 'background-opacity': 1, - 'background-image': 'none', - 'background-image-opacity': 1, - 'background-position-x': '50%', - 'background-position-y': '50%', - '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', - - // compound props - 'padding-top': 0, - 'padding-bottom': 0, - 'padding-left': 0, - 'padding-right': 0, - 'position': 'origin', - 'compound-sizing-wrt-labels': 'include', - }, { - // 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; - - css[ name ] = val; - } - - return css; - }, {}), { - // edge props - 'line-style': 'solid', - 'line-color': '#ddd', - 'control-point-step-size': 40, - 'control-point-weights': 0.5, - 'segment-weights': 0.25, - 'segment-distances': 20, - 'curve-style': 'bezier', - 'haystack-radius': 0.8 - }, [ - { name: 'arrow-shape', value: 'none' }, - { name: 'arrow-color', value: '#ddd' }, - { name: 'arrow-fill', value: 'filled' } - ].reduce(function( css, prop ){ - styfn.arrowPrefixes.forEach(function( prefix ){ - var name = prefix + '-' + prop.name; - var val = prop.value; - - css[ name ] = val; - }); - - return css; - }, {}) ) ) - .selector('$node > node') // compound (parent) node properties - .css({ - 'width': 'auto', - 'height': 'auto', - 'shape': 'rectangle', - 'padding-top': 10, - 'padding-right': 10, - 'padding-left': 10, - 'padding-bottom': 10 - }) - .selector('edge') // just edge properties - .css({ - 'width': 1 - }) - .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 - }) - ; - - this.defaultLength = this.length; -}; - -module.exports = styfn; - -},{"../util":94}],90:[function(_dereq_,module,exports){ -'use strict'; - -var util = _dereq_('../util'); -var Selector = _dereq_('../selector'); - -var styfn = {}; - -styfn.applyFromString = function( string ){ - var self = this; - var style = this; - var remaining = '' + string; - var selAndBlockStr; - var blockRem; - var propAndValStr; - - // remove comments from the style string - remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, ''); - - 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 = ''; - } - } - - 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 = ''; - } - } - - while(true){ - var nothingLeftToParse = remaining.match(/^\s*$/); - if( nothingLeftToParse ){ break; } - - 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; - } - - 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; - } - } - - // parse the block of properties and values - var blockStr = selAndBlock[2]; - var invalidBlock = false; - blockRem = blockStr; - var props = []; - - while(true){ - var nothingLeftToParse = blockRem.match(/^\s*$/); - if( nothingLeftToParse ){ break; } - - var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/); - - if( !propAndVal ){ - util.error('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr); - invalidBlock = true; - break; - } - - propAndValStr = propAndVal[0]; - var propStr = propAndVal[1]; - var valStr = propAndVal[2]; - - var prop = self.properties[ propStr ]; - if( !prop ){ - util.error('Skipping property: Invalid property name in: ' + propAndValStr); - - // skip this property in the block - removePropAndValFromRem(); - continue; - } - - 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; - } - - props.push({ - name: propStr, - val: valStr - }); - removePropAndValFromRem(); - } - - if( invalidBlock ){ - removeSelAndBlockFromRemaining(); - break; - } - - // 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 ); - } - - removeSelAndBlockFromRemaining(); - } - - return style; -}; - -styfn.fromString = function( string ){ - var style = this; - - style.resetToDefault(); - style.applyFromString( string ); - - return style; -}; - -module.exports = styfn; - -},{"../selector":81,"../util":94}],91:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('./is'); -var util = _dereq_('./util'); -var Style = _dereq_('./style'); - -// a dummy stylesheet object that doesn't need a reference to the core -// (useful for init) -var Stylesheet = function(){ - 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) ]; - } - - if( mapVal !== undefined ){ - var name = prop.name; - var value = mapVal; - - this[i].properties.push({ - name: name, - value: value - }); - } - } - } - - 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); - - for( var i = 0; i < this.length; i++ ){ - var context = this[i]; - var selector = context.selector; - var props = context.properties; - - style.selector(selector); // apply selector - - for( var j = 0; j < props.length; j++ ){ - var prop = props[j]; - - style.css( prop.name, prop.value ); // apply property - } - } - - return style; -}; - -module.exports = Stylesheet; - -},{"./is":77,"./style":86,"./util":94}],92:[function(_dereq_,module,exports){ -// cross-env thread/worker -// NB : uses (heavyweight) processes on nodejs so best not to create too many threads - -'use strict'; - -var window = _dereq_('./window'); -var util = _dereq_('./util'); -var Promise = _dereq_('./promise'); -var Event = _dereq_('./event'); -var define = _dereq_('./define'); -var is = _dereq_('./is'); - -var Thread = function( opts ){ - if( !(this instanceof Thread) ){ - return new Thread( opts ); - } - - var _p = this._private = { - requires: [], - files: [], - queue: null, - pass: [], - disabled: false - }; - - if( is.plainObject(opts) ){ - if( opts.disabled != null ){ - _p.disabled = !!opts.disabled; - } - } - -}; - -var thdfn = Thread.prototype; // short alias - -var stringifyFieldVal = function( val ){ - var valStr = is.fn( val ) ? val.toString() : 'JSON.parse("' + JSON.stringify(val) + '")'; - - return valStr; -}; - -// allows for requires with prototypes and subobjs etc -var fnAsRequire = function( fn ){ - var req; - var fnName; - - if( is.object(fn) && fn.fn ){ // manual fn - req = fnAs( fn.fn, fn.name ); - fnName = fn.name; - fn = fn.fn; - } else if( is.fn(fn) ){ // auto fn - req = fn.toString(); - fnName = fn.name; - } else if( is.string(fn) ){ // stringified fn - req = fn; - } else if( is.object(fn) ){ // plain object - if( fn.proto ){ - req = ''; - } else { - req = fn.name + ' = {};'; - } - - fnName = fn.name; - fn = fn.obj; - } - - req += '\n'; - - var protoreq = function( val, subname ){ - if( val.prototype ){ - var protoNonempty = false; - for( var prop in val.prototype ){ protoNonempty = true; break; } // jshint ignore:line - - if( protoNonempty ){ - req += fnAsRequire( { - name: subname, - obj: val, - proto: true - }, val ); - } - } - }; - - // pull in prototype - if( fn.prototype && fnName != null ){ - - for( var name in fn.prototype ){ - var protoStr = ''; - - var val = fn.prototype[ name ]; - var valStr = stringifyFieldVal( val ); - var subname = fnName + '.prototype.' + name; - - protoStr += subname + ' = ' + valStr + ';\n'; - - if( protoStr ){ - req += protoStr; - } - - protoreq( val, subname ); // subobject with prototype - } - - } - - // pull in properties for obj/fns - if( !is.string(fn) ){ for( var name in fn ){ - var propsStr = ''; - - if( fn.hasOwnProperty(name) ){ - var val = fn[ name ]; - var valStr = stringifyFieldVal( val ); - var subname = fnName + '["' + name + '"]'; - - propsStr += subname + ' = ' + valStr + ';\n'; - } - - if( propsStr ){ - req += propsStr; - } - - protoreq( val, subname ); // subobject with prototype - } } - - return req; -}; - -var isPathStr = function( str ){ - return is.string(str) && str.match(/\.js$/); -}; - -util.extend(thdfn, { - - instanceString: function(){ return 'thread'; }, - - require: function( fn, as ){ - var requires = this._private.requires; - - if( isPathStr(fn) ){ - this._private.files.push( fn ); - - return this; - } - - if( as ){ - if( is.fn(fn) ){ - fn = { name: as, fn: fn }; - } else { - fn = { name: as, obj: fn }; - } - } else { - if( is.fn(fn) ){ - if( !fn.name ){ - throw 'The function name could not be automatically determined. Use thread.require( someFunction, "someFunction" )'; - } - - fn = { name: fn.name, fn: fn }; - } - } - - requires.push( fn ); - - return this; // chaining - }, - - pass: function( data ){ - this._private.pass.push( data ); - - return this; // chaining - }, - - run: function( fn, pass ){ // fn used like main() - var self = this; - var _p = this._private; - pass = pass || _p.pass.shift(); - - if( _p.stopped ){ - throw 'Attempted to run a stopped thread! Start a new thread or do not stop the existing thread and reuse it.'; - } - - if( _p.running ){ - return ( _p.queue = _p.queue.then(function(){ // inductive step - return self.run( fn, pass ); - }) ); - } - - var useWW = window != null && !_p.disabled; - var useNode = !window && typeof module !== 'undefined' && !_p.disabled; - - self.trigger('run'); - - var runP = new Promise(function( resolve, reject ){ - - _p.running = true; - - var threadTechAlreadyExists = _p.ran; - - var fnImplStr = is.string( fn ) ? fn : fn.toString(); - - // worker code to exec - var fnStr = '\n' + ( _p.requires.map(function( r ){ - return fnAsRequire( r ); - }) ).concat( _p.files.map(function( f ){ - if( useWW ){ - var wwifyFile = function( file ){ - if( file.match(/^\.\//) || file.match(/^\.\./) ){ - return window.location.origin + window.location.pathname + file; - } else if( file.match(/^\//) ){ - return window.location.origin + '/' + file; - } - return file; - }; - - return 'importScripts("' + wwifyFile(f) + '");'; - } else if( useNode ) { - return 'eval( require("fs").readFileSync("' + f + '", { encoding: "utf8" }) );'; - } else { - throw 'External file `' + f + '` can not be required without any threading technology.'; - } - }) ).concat([ - '( function(){', - 'var ret = (' + fnImplStr + ')(' + JSON.stringify(pass) + ');', - 'if( ret !== undefined ){ resolve(ret); }', // assume if ran fn returns defined value (incl. null), that we want to resolve to it - '} )()\n' - ]).join('\n'); - - // because we've now consumed the requires, empty the list so we don't dupe on next run() - _p.requires = []; - _p.files = []; - - if( useWW ){ - var fnBlob, fnUrl; - - // add normalised thread api functions - if( !threadTechAlreadyExists ){ - var fnPre = fnStr + ''; - - fnStr = [ - 'function _ref_(o){ return eval(o); };', - 'function broadcast(m){ return message(m); };', // alias - 'function message(m){ postMessage(m); };', - 'function listen(fn){', - ' self.addEventListener("message", function(m){ ', - ' if( typeof m === "object" && (m.data.$$eval || m.data === "$$start") ){', - ' } else { ', - ' fn( m.data );', - ' }', - ' });', - '};', - 'self.addEventListener("message", function(m){ if( m.data.$$eval ){ eval( m.data.$$eval ); } });', - 'function resolve(v){ postMessage({ $$resolve: v }); };', - 'function reject(v){ postMessage({ $$reject: v }); };' - ].join('\n'); - - fnStr += fnPre; - - fnBlob = new Blob([ fnStr ], { - type: 'application/javascript' - }); - fnUrl = window.URL.createObjectURL( fnBlob ); - } - // create webworker and let it exec the serialised code - var ww = _p.webworker = _p.webworker || new Worker( fnUrl ); - - if( threadTechAlreadyExists ){ // then just exec new run() code - ww.postMessage({ - $$eval: fnStr - }); - } - - // worker messages => events - var cb; - ww.addEventListener('message', cb = function( m ){ - var isObject = is.object(m) && is.object( m.data ); - - if( isObject && ('$$resolve' in m.data) ){ - ww.removeEventListener('message', cb); // done listening b/c resolve() - - resolve( m.data.$$resolve ); - } else if( isObject && ('$$reject' in m.data) ){ - ww.removeEventListener('message', cb); // done listening b/c reject() - - reject( m.data.$$reject ); - } else { - self.trigger( new Event(m, { type: 'message', message: m.data }) ); - } - }, false); - - if( !threadTechAlreadyExists ){ - ww.postMessage('$$start'); // start up the worker - } - - } else if( useNode ){ - // create a new process - - if( !_p.child ){ - _p.child = ( _dereq_('child_process').fork( _dereq_('path').join(__dirname, 'thread-node-fork') ) ); - } - - var child = _p.child; - - // child process messages => events - var cb; - child.on('message', cb = function( m ){ - if( is.object(m) && ('$$resolve' in m) ){ - child.removeListener('message', cb); // done listening b/c resolve() - - resolve( m.$$resolve ); - } else if( is.object(m) && ('$$reject' in m) ){ - child.removeListener('message', cb); // done listening b/c reject() - - reject( m.$$reject ); - } else { - self.trigger( new Event({}, { type: 'message', message: m }) ); - } - }); - - // ask the child process to eval the worker code - child.send({ - $$eval: fnStr - }); - - } else { // use a fallback mechanism using a timeout - - var promiseResolve = resolve; - var promiseReject = reject; - - var timer = _p.timer = _p.timer || { - - listeners: [], - - exec: function(){ - // as a string so it can't be mangled by minifiers and processors - fnStr = [ - 'function _ref_(o){ return eval(o); };', - 'function broadcast(m){ return message(m); };', - 'function message(m){ self.trigger( new Event({}, { type: "message", message: m }) ); };', - 'function listen(fn){ timer.listeners.push( fn ); };', - 'function resolve(v){ promiseResolve(v); };', - 'function reject(v){ promiseReject(v); };' - ].join('\n') + fnStr; - - // the .run() code - eval( fnStr ); // jshint ignore:line - }, - - message: function( m ){ - var ls = timer.listeners; - - for( var i = 0; i < ls.length; i++ ){ - var fn = ls[i]; - - fn( m ); - } - } - - }; - - timer.exec(); - } - - }).then(function( v ){ - _p.running = false; - _p.ran = true; - - self.trigger('ran'); - - return v; - }); - - if( _p.queue == null ){ - _p.queue = runP; // i.e. first step of inductive promise chain (for queue) - } - - return runP; - }, - - // send the thread a message - message: function( m ){ - var _p = this._private; - - if( _p.webworker ){ - _p.webworker.postMessage( m ); - } - - if( _p.child ){ - _p.child.send( m ); - } - - if( _p.timer ){ - _p.timer.message( m ); - } - - return this; // chaining - }, - - stop: function(){ - var _p = this._private; - - if( _p.webworker ){ - _p.webworker.terminate(); - } - - if( _p.child ){ - _p.child.kill(); - } - - if( _p.timer ){ - // nothing we can do if we've run a timeout - } - - _p.stopped = true; - - return this.trigger('stop'); // chaining - }, - - stopped: function(){ - return this._private.stopped; - } - -}); - -// turns a stringified function into a (re)named function -var fnAs = function( fn, name ){ - var fnStr = fn.toString(); - fnStr = fnStr.replace(/function\s*?\S*?\s*?\(/, 'function ' + name + '('); - - return fnStr; -}; - -var defineFnal = function( opts ){ - opts = opts || {}; - - return function fnalImpl( fn, arg1 ){ - var fnStr = fnAs( fn, '_$_$_' + opts.name ); - - this.require( fnStr ); - - return this.run( [ - 'function( data ){', - ' var origResolve = resolve;', - ' var res = [];', - ' ', - ' resolve = function( val ){', - ' res.push( val );', - ' };', - ' ', - ' var ret = data.' + opts.name + '( _$_$_' + opts.name + ( arguments.length > 1 ? ', ' + JSON.stringify(arg1) : '' ) + ' );', - ' ', - ' resolve = origResolve;', - ' resolve( res.length > 0 ? res : ret );', - '}' - ].join('\n') ); - }; -}; - -util.extend(thdfn, { - reduce: defineFnal({ name: 'reduce' }), - - reduceRight: defineFnal({ name: 'reduceRight' }), - - map: defineFnal({ name: 'map' }) -}); - -// aliases -var fn = thdfn; -fn.promise = fn.run; -fn.terminate = fn.halt = fn.stop; -fn.include = fn.require; - -// pull in event apis -util.extend(thdfn, { - on: define.on(), - one: define.on({ unbindSelfOnTrigger: true }), - off: define.off(), - trigger: define.trigger() -}); - -define.eventAliasesOn( thdfn ); - -module.exports = Thread; - -},{"./define":41,"./event":42,"./is":77,"./promise":80,"./util":94,"./window":100,"child_process":undefined,"path":undefined}],93:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); - -module.exports = { - // get [r, g, b] from #abc or #aabbcc - hex2tuple: function( hex ){ - if( !(hex.length === 4 || hex.length === 7) || hex[0] !== "#" ){ return; } - - var shortHex = hex.length === 4; - var r, g, b; - var base = 16; - - 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 ); - } - - return [r, g, b]; - }, - - // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0) - hsl2tuple: function( hsl ){ - var ret; - var h, s, l, a, r, g, b; - 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; - } - - 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] - - a = m[4]; - if( a !== undefined ){ - a = parseFloat( a ); - - if( a < 0 || a > 1 ){ return; } // alpha is [0, 1] - } - - // 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) ); - } - - ret = [r, g, b, a]; - } - - return ret; - }, - - // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0) - rgb2tuple: function( rgb ){ - var ret; - - var m = new RegExp("^" + this.regex.rgba + "$").exec(rgb); - if( m ){ - ret = []; - - var isPct = []; - for( var i = 1; i <= 3; i++ ){ - var channel = m[i]; - - if( channel[ channel.length - 1 ] === "%" ){ - isPct[i] = true; - } - channel = parseFloat( channel ); - - if( isPct[i] ){ - channel = channel/100 * 255; // normalise to [0, 255] - } - - if( channel < 0 || channel > 255 ){ return; } // invalid channel value - - ret.push( Math.floor(channel) ); - } - - 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 - - var alpha = m[4]; - if( alpha !== undefined ){ - alpha = parseFloat( alpha ); - - if( alpha < 0 || alpha > 1 ){ return; } // invalid alpha value - - ret.push( alpha ); - } - } - - return ret; - }, - - colorname2tuple: function( color ){ - return this.colors[ color.toLowerCase() ]; - }, - - color2tuple: function( color ){ - return ( is.array(color) ? color : null ) - || this.colorname2tuple(color) - || this.hex2tuple(color) - || this.rgb2tuple(color) - || this.hsl2tuple(color); - }, - - colors: { - // special colour names - transparent: [0, 0, 0, 0], // NB alpha === 0 - - // 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] - } -}; - -},{"../is":77}],94:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); -var math = _dereq_('../math'); - -var util = { - - falsify: function(){ return false; }, - - zeroify: function(){ return 0; }, - - noop: function(){}, - - /* jshint ignore:start */ - error: function( msg ){ - if( console.error ){ - console.error.apply( console, arguments ); - - if( console.trace ){ console.trace(); } - } else { - console.log.apply( console, arguments ); - - if( console.trace ){ console.trace(); } - } - }, - /* jshint ignore:end */ - - clone: function( obj ){ - return this.extend( {}, obj ); - }, - - // gets a shallow copy of the argument - copy: function( obj ){ - if( obj == null ){ - return obj; - } if( is.array(obj) ){ - return obj.slice(); - } else if( is.plainObject(obj) ){ - return this.clone( obj ); - } else { - return obj; - } - } - -}; - -util.makeBoundingBox = math.makeBoundingBox.bind( math ); - -util._staticEmptyObject = {}; - -util.staticEmptyObject = function(){ - return util._staticEmptyObject; -}; - -util.extend = Object.assign != null ? Object.assign : function( tgt ){ - var args = arguments; - - for( var i = 1; i < args.length; i++ ){ - var obj = args[i]; - - for( var k in obj ){ - tgt[k] = obj[k]; - } - } - - return tgt; -}; - -[ - _dereq_('./colors'), - _dereq_('./maps'), - { memoize: _dereq_('./memoize') }, - _dereq_('./regex'), - _dereq_('./strings'), - _dereq_('./timing') -].forEach(function( req ){ - util.extend( util, req ); -}); - -module.exports = util; - -},{"../is":77,"../math":79,"./colors":93,"./maps":95,"./memoize":96,"./regex":97,"./strings":98,"./timing":99}],95:[function(_dereq_,module,exports){ -'use strict'; - -var is = _dereq_('../is'); - -module.exports = { - // has anything been set in the map - mapEmpty: function( map ){ - var empty = true; - - if( map != null ){ - for(var i in map){ // jshint ignore:line - empty = false; - break; - } - } - - return empty; - }, - - // pushes to the array at the end of a map (map may not be built) - pushMap: function( options ){ - var array = this.getMap(options); - - if( array == null ){ // if empty, put initial array - this.setMap( this.extend({}, options, { - value: [ options.value ] - }) ); - } else { - array.push( options.value ); - } - }, - - // sets the value in a map (map may not be built) - setMap: function( options ){ - var obj = options.map; - var key; - var keys = options.keys; - var l = keys.length; - - for(var i = 0; i < l; i++){ - var key = keys[i]; - - if( is.plainObject( key ) ){ - this.error('Tried to set map with object key'); - } - - if( i < keys.length - 1 ){ - - // extend the map if necessary - if( obj[key] == null ){ - obj[key] = {}; - } - - obj = obj[key]; - } else { - // set the value - obj[key] = options.value; - } - } - }, - - // gets the value in a map even if it's not built in places - getMap: function( options ){ - var obj = options.map; - var keys = options.keys; - var l = keys.length; - - for(var i = 0; i < l; i++){ - var key = keys[i]; - - if( is.plainObject( key ) ){ - this.error('Tried to get map with object key'); - } - - obj = obj[key]; - - if( obj == null ){ - return obj; - } - } - - return obj; - }, - - // deletes the entry in the map - deleteMap: function( options ){ - var obj = options.map; - var keys = options.keys; - var l = keys.length; - var keepChildren = options.keepChildren; - - for(var i = 0; i < l; i++){ - var key = keys[i]; - - if( is.plainObject( key ) ){ - this.error('Tried to delete map with object key'); - } - - var lastKey = i === options.keys.length - 1; - if( lastKey ){ - - if( keepChildren ){ // then only delete child fields not in keepChildren - for( var child in obj ){ - if( !keepChildren[child] ){ - obj[child] = undefined; - } - } - } else { - obj[key] = undefined; - } - - } else { - obj = obj[key]; - } - } - } -}; - -},{"../is":77}],96:[function(_dereq_,module,exports){ -'use strict'; - -module.exports = function memoize( fn, keyFn ){ - var self = this; - var cache = {}; - - if( !keyFn ){ - keyFn = function(){ - if( arguments.length === 1 ){ - return arguments[0]; - } - - var args = []; - - for( var i = 0; i < arguments.length; i++ ){ - args.push( arguments[i] ); - } - - return args.join('$'); - }; - } - - return function memoizedFn(){ - var args = arguments; - var ret; - var k = keyFn.apply( self, args ); - - if( !(ret = cache[k]) ){ - ret = cache[k] = fn.apply( self, args ); - } - - return ret; - }; -}; - -},{}],97:[function(_dereq_,module,exports){ -'use strict'; - -var number = "(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))"; - -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 +"))?\\)"; - -var hex3 = "\\#[0-9a-fA-F]{3}"; -var hex6 = "\\#[0-9a-fA-F]{6}"; - -module.exports = { - regex: { - number: number, - rgba: rgba, - rgbaNoBackRefs: rgbaNoBackRefs, - hsla: hsla, - hslaNoBackRefs: hslaNoBackRefs, - hex3: hex3, - hex6: hex6 - } -}; - -},{}],98:[function(_dereq_,module,exports){ -'use strict'; - -var memoize = _dereq_('./memoize'); -var is = _dereq_('../is'); - -module.exports = { - - camel2dash: memoize( function( str ){ - return str.replace(/([A-Z])/g, function( v ){ - return '-' + v.toLowerCase(); - }); - } ), - - dash2camel: memoize( function( str ){ - return str.replace(/(-\w)/g, function( v ){ - return v[1].toUpperCase(); - }); - } ), - - capitalize: function(str){ - if( is.emptyString(str) ){ - return str; - } - - return str.charAt(0).toUpperCase() + str.substring(1); - } - -}; - -},{"../is":77,"./memoize":96}],99:[function(_dereq_,module,exports){ -'use strict'; - -var window = _dereq_('../window'); -var is = _dereq_('../is'); -var performance = window ? window.performance : null; - -var util = {}; - -var raf = !window ? null : ( window.requestAnimationFrame || window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ); - -raf = raf || function( fn ){ - if( fn ){ - setTimeout(function(){ - fn( pnow() ); - }, 1000/60); - } -}; - -util.requestAnimationFrame = function(fn){ - raf( fn ); -}; - -var pnow = performance && performance.now ? function(){ return performance.now(); } : function(){ return Date.now(); }; - -util.performanceNow = pnow; - -// ported lodash throttle function -util.throttle = function(func, wait, options) { - var leading = true, - trailing = true; - - if (options === false) { - leading = false; - } else if (is.plainObject(options)) { - leading = 'leading' in options ? options.leading : leading; - trailing = 'trailing' in options ? options.trailing : trailing; - } - options = options || {}; - options.leading = leading; - options.maxWait = wait; - options.trailing = trailing; - - return util.debounce(func, wait, options); -}; - -util.now = function(){ - return Date.now(); -}; - -util.debounce = function(func, wait, options) { // ported lodash debounce function - var util = this; - var args, - maxTimeoutId, - result, - stamp, - thisArg, - timeoutId, - trailingCall, - lastCalled = 0, - maxWait = false, - trailing = true; - - if (!is.fn(func)) { - return; - } - wait = Math.max(0, wait) || 0; - if (options === true) { - var leading = true; - trailing = false; - } else if (is.plainObject(options)) { - leading = options.leading; - maxWait = 'maxWait' in options && (Math.max(wait, options.maxWait) || 0); - trailing = 'trailing' in options ? options.trailing : trailing; - } - var delayed = function() { - var remaining = wait - (util.now() - stamp); - if (remaining <= 0) { - if (maxTimeoutId) { - clearTimeout(maxTimeoutId); - } - var isCalled = trailingCall; - maxTimeoutId = timeoutId = trailingCall = undefined; - if (isCalled) { - lastCalled = util.now(); - result = func.apply(thisArg, args); - if (!timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - } - } else { - timeoutId = setTimeout(delayed, remaining); - } - }; - - var maxDelayed = function() { - if (timeoutId) { - clearTimeout(timeoutId); - } - maxTimeoutId = timeoutId = trailingCall = undefined; - if (trailing || (maxWait !== wait)) { - lastCalled = util.now(); - result = func.apply(thisArg, args); - if (!timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - } - }; - - return function() { - args = arguments; - stamp = util.now(); - thisArg = this; - trailingCall = trailing && (timeoutId || !leading); - - if (maxWait === false) { - var leadingCall = leading && !timeoutId; - } else { - if (!maxTimeoutId && !leading) { - lastCalled = stamp; - } - var remaining = maxWait - (stamp - lastCalled), - isCalled = remaining <= 0; - - if (isCalled) { - if (maxTimeoutId) { - maxTimeoutId = clearTimeout(maxTimeoutId); - } - lastCalled = stamp; - result = func.apply(thisArg, args); - } - else if (!maxTimeoutId) { - maxTimeoutId = setTimeout(maxDelayed, remaining); - } - } - if (isCalled && timeoutId) { - timeoutId = clearTimeout(timeoutId); - } - else if (!timeoutId && wait !== maxWait) { - timeoutId = setTimeout(delayed, wait); - } - if (leadingCall) { - isCalled = true; - result = func.apply(thisArg, args); - } - if (isCalled && !timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - return result; - }; -}; - -module.exports = util; - -},{"../is":77,"../window":100}],100:[function(_dereq_,module,exports){ -module.exports = ( typeof window === 'undefined' ? null : window ); - -},{}]},{},[76])(76) -}); - - -//# sourceMappingURL=cytoscape.js.map diff --git a/docs/htmldoc/js/cytoscape.js-panzoom.css b/docs/htmldoc/js/cytoscape.js-panzoom.css deleted file mode 100644 index 46d9a36..0000000 --- a/docs/htmldoc/js/cytoscape.js-panzoom.css +++ /dev/null @@ -1,203 +0,0 @@ -.cy-panzoom { - position: fixed; - right: 70px; - top: 10px; - font-size: 13px; - color: #fff; - font-family: arial, helvetica, sans-serif; - line-height: 1; - color: #666; - font-size: 11px; - z-index: 99999; -} - -.cy-panzoom-zoom-button { - cursor: pointer; - padding: 3px; - text-align: center; - position: absolute; - border-radius: 3px; - width: 10px; - height: 10px; - left: 16px; - background: #fff; - border: 1px solid #999; - margin-left: -1px; - margin-top: -1px; - z-index: 1; -} - -.cy-panzoom-zoom-button:active, -.cy-panzoom-slider-handle:active, -.cy-panzoom-slider-handle.active { - background: #ddd; -} - -.cy-panzoom-pan-button { - position: absolute; - z-index: 1; - height: 16px; - width: 16px; -} - -.cy-panzoom-reset { - top: 55px; -} - -.cy-panzoom-zoom-in { - top: 80px; -} - -.cy-panzoom-zoom-out { - top: 197px; -} - -.cy-panzoom-pan-up { - top: 0; - left: 50%; - margin-left: -5px; - width: 0; - height: 0; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-bottom: 5px solid #666; -} - -.cy-panzoom-pan-down { - bottom: 0; - left: 50%; - margin-left: -5px; - width: 0; - height: 0; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-top: 5px solid #666; -} - -.cy-panzoom-pan-left { - top: 50%; - left: 0; - margin-top: -5px; - width: 0; - height: 0; - border-top: 5px solid transparent; - border-bottom: 5px solid transparent; - border-right: 5px solid #666; -} - -.cy-panzoom-pan-right { - top: 50%; - right: 0; - margin-top: -5px; - width: 0; - height: 0; - border-top: 5px solid transparent; - border-bottom: 5px solid transparent; - border-left: 5px solid #666; -} - -.cy-panzoom-pan-indicator { - position: absolute; - left: 0; - top: 0; - width: 8px; - height: 8px; - border-radius: 8px; - background: #000; - border-radius: 8px; - margin-left: -5px; - margin-top: -5px; - display: none; - z-index: 999; - opacity: 0.6; -} - -.cy-panzoom-slider { - position: absolute; - top: 97px; - left: 17px; - height: 100px; - width: 15px; -} - -.cy-panzoom-slider-background { - position: absolute; - top: 0; - width: 2px; - height: 100px; - left: 5px; - background: #fff; - border-left: 1px solid #999; - border-right: 1px solid #999; -} - -.cy-panzoom-slider-handle { - position: absolute; - width: 16px; - height: 8px; - background: #fff; - border: 1px solid #999; - border-radius: 2px; - margin-left: -2px; - z-index: 999; - line-height: 8px; - cursor: default; -} - -.cy-panzoom-slider-handle .icon { - margin: 0 4px; - line-height: 10px; -} - -.cy-panzoom-no-zoom-tick { - position: absolute; - background: #666; - border: 1px solid #fff; - border-radius: 2px; - margin-left: -1px; - width: 8px; - height: 2px; - left: 3px; - z-index: 1; - margin-top: 3px; -} - -.cy-panzoom-panner { - position: absolute; - left: 5px; - top: 5px; - height: 40px; - width: 40px; - background: #fff; - border: 1px solid #999; - border-radius: 40px; - margin-left: -1px; -} - -.cy-panzoom-panner-handle { - position: absolute; - left: 0; - top: 0; - outline: none; - height: 40px; - width: 40px; - position: absolute; - z-index: 999; -} - -.cy-panzoom-zoom-only .cy-panzoom-slider, -.cy-panzoom-zoom-only .cy-panzoom-panner { - display: none; -} - -.cy-panzoom-zoom-only .cy-panzoom-reset { - top: 20px; -} - -.cy-panzoom-zoom-only .cy-panzoom-zoom-in { - top: 45px; -} - -.cy-panzoom-zoom-only .cy-panzoom-zoom-out { - top: 70px; -} diff --git a/docs/htmldoc/js/cytoscape.min.js b/docs/htmldoc/js/cytoscape.min.js deleted file mode 100644 index a498936..0000000 --- a/docs/htmldoc/js/cytoscape.min.js +++ /dev/null @@ -1,26 +0,0 @@ -/*! - * This file is part of Cytoscape.js 2.5.1. - * - * Cytoscape.js is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * Cytoscape.js is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * Cytoscape.js. If not, see . - */ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.cytoscape=e()}}(function(){var define,module,exports;return function e(t,r,n){function i(o,s){if(!r[o]){if(!t[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(a)return a(o,!0);var u=new Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var c=r[o]={exports:{}};t[o][0].call(c.exports,function(e){var r=t[o][1][e];return i(r?r:e)},c,c.exports,e,t,r,n)}return r[o].exports}for(var a="function"==typeof require&&require,o=0;oa&&(n=a,r=i)}return r},a=this._private.cy;if(null==e||null==e.root)return void 0;var o=n.string(e.root)?this.filter(e.root)[0]:e.root[0];if(null==e.goal)return void 0;var s=n.string(e.goal)?this.filter(e.goal)[0]:e.goal[0];if(null!=e.heuristic&&n.fn(e.heuristic))var l=e.heuristic;else var l=function(){return 0};if(null!=e.weight&&n.fn(e.weight))var u=e.weight;else var u=function(e){return 1};if(null!=e.directed)var c=e.directed;else var c=!1;var d=[],h=[o.id()],p={},v={},f={},g={};f[o.id()]=0,g[o.id()]=l(o);for(var y=this.edges().stdFilter(function(e){return!e.isLoop()}),m=this.nodes(),b=0;h.length>0;){var x=i(h,g),w=a.getElementById(h[x]);if(b++,w.id()==s.id()){var _=r(o.id(),s.id(),p,[]);return _.reverse(),{found:!0,distance:f[w.id()],path:t.spawn(_),steps:b}}d.push(w.id()),h.splice(x,1);var E=w.connectedEdges();c&&(E=E.stdFilter(function(e){return e.data("source")===w.id()})),E=E.intersect(y);for(var D=0;Dh;h++)d[u[h].id()]=h;for(var p=[],v=[],f=[],h=0;c>h;h++)u[h].id()===o.id()?p[h]=0:p[h]=1/0,v[h]=void 0;for(var g=!1,h=1;c>h;h++){g=!1;for(var y=0;yh;h++)_.push(u[h].id());var E={distanceTo:function(e){if(n.string(e))var t=s.filter(e)[0].id();else var t=e.id();return p[d[t]]},pathTo:function(e){var r=function(e,t,r,n,i,a){for(;;){if(i.push(s.getElementById(n[r])),i.push(a[r]),t===r)return i;var o=e[r];if("undefined"==typeof o)return void 0;r=o}};if(n.string(e))var i=s.filter(e)[0].id();else var i=e.id();var a=[],l=r(v,d[o.id()],d[i],_,a,f);return null!=l&&l.reverse(),t.spawn(l)},hasNegativeWeightCycle:!1};return E}};t.exports=a},{"../../is":77,"../../util":94}],4:[function(e,t,r){"use strict";var n=e("../../is"),i={betweennessCentrality:function(e){if(e=e||{},null!=e.weight&&n.fn(e.weight))var t=e.weight,r=!0;else var r=!1;if(null!=e.directed&&n.bool(e.directed))var i=e.directed;else var i=!1;for(var a=function(e,t){e.unshift(t);for(var r=0;f[e[r]]0;){var y=g.pop();h.push(y),r?l[y].forEach(function(e){if(o.$("#"+y).edgesTo(e).length>0)var r=o.$("#"+y).edgesTo(e)[0];else var r=e.edgesTo("#"+y)[0];var n=t.apply(r,[r]);f[e.id()]>f[y]+n&&(f[e.id()]=f[y]+n,g.indexOf(e.id())<0?a(g,e.id()):(g.splice(g.indexOf(e.id()),1),a(g,e.id())),v[e.id()]=0,p[e.id()]=[]),f[e.id()]==f[y]+n&&(v[e.id()]=v[e.id()]+v[y],p[e.id()].push(y))}):l[y].forEach(function(e){f[e.id()]==Number.POSITIVE_INFINITY&&(g.unshift(e.id()),f[e.id()]=f[y]+1),f[e.id()]==f[y]+1&&(v[e.id()]=v[e.id()]+v[y],p[e.id()].push(y))})}for(var m={},c=0;c0;){var b=h.pop();p[b].forEach(function(e){m[e]=m[e]+v[e]/v[b]*(1+m[b]),b!=s[d].id()&&(u[b]=u[b]+m[b])})}}var x=0;for(var w in u)xu||!i)&&(o=u,i=l)}return{edge:i,dist:o}};f.size()>0;){var b=f.pop(),x=p(b),w=b.id();if(c[w]=x,x===Math.Infinite)break;for(var _=b.neighborhood().intersect(h),g=0;g<_.length;g++){var E=_[g],D=E.id(),S=m(b,E),k=x+S.dist;k0)for(r.unshift(t);u[i.id()];){var a=u[i.id()];r.unshift(a.edge),r.unshift(a.node),i=a.node}return o.collection(r)}}}};o.bfs=o.breadthFirstSearch,o.dfs=o.depthFirstSearch,t.exports=o},{"../../heap":75,"../../is":77}],6:[function(e,t,r){"use strict";var n=e("../../is"),i={closenessCentralityNormalized:function(e){e=e||{};var t=this.cy(),r=e.harmonic;void 0===r&&(r=!0);for(var i={},a=0,o=this.nodes(),s=this.floydWarshall({weight:e.weight,directed:e.directed}),l=0;la&&(a=u),i[o[l].id()]=u}return{closeness:function(e){if(n.string(e))var e=t.filter(e)[0].id();else var e=e.id();return i[e]/a}}},closenessCentrality:function(e){if(e=e||{},null==e.root)return void 0;if(n.string(e.root))var t=this.filter(e.root)[0];else var t=e.root[0];if(null!=e.weight&&n.fn(e.weight))var r=e.weight;else var r=function(){return 1};if(null!=e.directed&&n.bool(e.directed))var i=e.directed;else var i=!1;var a=e.harmonic;void 0===a&&(a=!0);for(var o=this.dijkstra({root:t,weight:r,directed:i}),s=0,l=this.nodes(),u=0;ud;d++){var h=a[d],p=this.degreeCentrality(i.extend({},e,{root:h}));ud;d++){var h=a[d],p=this.degreeCentrality(i.extend({},e,{root:h}));fu;u++)l[o[u].id()]=u;for(var c=[],u=0;s>u;u++){for(var d=new Array(s),h=0;s>h;h++)u==h?d[h]=0:d[h]=1/0;c.push(d)}var p=[],v=[],f=function(e){for(var t=0;s>t;t++){for(var r=new Array(s),n=0;s>n;n++)r[n]=void 0;e.push(r)}};f(p),f(v);for(var u=0;um&&(c[g][y]=m,p[g][y]=y,v[g][y]=a[u])}if(!i)for(var u=0;um&&(c[g][y]=m,p[g][y]=y,v[g][y]=a[u])}for(var b=0;s>b;b++)for(var u=0;s>u;u++)for(var h=0;s>h;h++)c[u][b]+c[b][h]u;u++)x.push(o[u].id());var w={distance:function(e,r){if(n.string(e))var i=t.filter(e)[0].id();else var i=e.id();if(n.string(r))var a=t.filter(r)[0].id();else var a=r.id();return c[l[i]][l[a]]},path:function(e,r){var i=function(e,r,n,i,a){if(e===r)return t.getElementById(i[e]);if(void 0===n[e][r])return void 0;for(var o=[t.getElementById(i[e])],s=e;e!==r;){s=e,e=n[e][r];var l=a[s][e];o.push(l),o.push(t.getElementById(i[e]))}return o};if(n.string(e))var a=t.filter(e)[0].id();else var a=e.id();if(n.string(r))var o=t.filter(r)[0].id();else var o=r.id();var s=i(l[a],l[o],p,x,v);return t.collection(s)}};return w}};t.exports=i},{"../../is":77}],9:[function(e,t,r){"use strict";var n=e("../../util"),i={};[e("./bfs-dfs"),e("./a-star"),e("./floyd-warshall"),e("./bellman-ford"),e("./kerger-stein"),e("./page-rank"),e("./degree-centrality"),e("./closeness-centrality"),e("./betweenness-centrality")].forEach(function(e){n.extend(i,e)}),t.exports=i},{"../../util":94,"./a-star":2,"./bellman-ford":3,"./betweenness-centrality":4,"./bfs-dfs":5,"./closeness-centrality":6,"./degree-centrality":7,"./floyd-warshall":8,"./kerger-stein":10,"./page-rank":11}],10:[function(e,t,r){"use strict";var n=e("../../util"),i={kargerStein:function(e){var t=this;e=e||{};var r=function(e,t,r){for(var n=r[e],i=n[1],a=n[2],o=t[i],s=t[a],l=r.filter(function(e){return t[e[1]]===o&&t[e[2]]===s?!1:t[e[1]]===s&&t[e[2]]===o?!1:!0}),u=0;u=n)return t;var o=Math.floor(Math.random()*t.length),s=r(o,e,t);return i(e,s,n-1,a)},a=this._private.cy,o=this.edges().stdFilter(function(e){return!e.isLoop()}),s=this.nodes(),l=s.length,u=o.length,c=Math.ceil(Math.pow(Math.log(l)/Math.LN2,2)),d=Math.floor(l/Math.sqrt(2));if(2>l)return void n.error("At least 2 nodes are required for Karger-Stein algorithm");for(var h={},p=0;l>p;p++)h[s[p].id()]=p;for(var v=[],p=0;u>p;p++){var f=o[p];v.push([p,h[f.source().id()],h[f.target().id()]])}for(var g,y=1/0,m=[],p=0;l>p;p++)m.push(p);for(var b=0;c>=b;b++){var x=m.slice(0),w=i(x,v,l,d),_=x.slice(0),E=i(x,w,d,2),D=i(_,w,d,2);E.length<=D.length&&E.lengthn;n++)r+=e[n];for(var n=0;t>n;n++)e[n]=e[n]/r};if(null!=e&&null!=e.dampingFactor)var r=e.dampingFactor;else var r=.8;if(null!=e&&null!=e.precision)var i=e.precision;else var i=1e-6;if(null!=e&&null!=e.iterations)var a=e.iterations;else var a=200;if(null!=e&&null!=e.weight&&n.fn(e.weight))var o=e.weight;else var o=function(e){return 1};for(var s=this._private.cy,l=this.edges().stdFilter(function(e){return!e.isLoop()}),u=this.nodes(),c=u.length,d=l.length,h={},p=0;c>p;p++)h[u[p].id()]=p;for(var v=[],f=[],g=(1-r)/c,p=0;c>p;p++){for(var y=[],m=0;c>m;m++)y.push(0);v.push(y),f.push(0)}for(var p=0;d>p;p++){var b=l[p],x=h[b.source().id()],w=h[b.target().id()],_=o.apply(b,[b]);v[w][x]+=_,f[x]+=_}for(var E=1/c+g,m=0;c>m;m++)if(0===f[m])for(var p=0;c>p;p++)v[p][m]=E;else for(var p=0;c>p;p++)v[p][m]=v[p][m]/f[m]+g;for(var D,S=[],k=[],p=0;c>p;p++)S.push(1),k.push(0);for(var T=0;a>T;T++){for(var P=k.slice(0),p=0;c>p;p++)for(var m=0;c>m;m++)P[p]+=v[p][m]*S[m];t(P),D=S,S=P;for(var C=0,p=0;c>p;p++)C+=Math.pow(D[p]-S[p],2);if(i>C)break}var N={rank:function(e){if(n.string(e))var t=s.filter(e)[0].id();else var t=e.id();return S[h[t]]}};return N}};t.exports=i},{"../../is":77}],12:[function(e,t,r){"use strict";var n=e("../define"),i={animate:n.animate(),animation:n.animation(),animated:n.animated(),clearQueue:n.clearQueue(),delay:n.delay(),delayAnimation:n.delayAnimation(),stop:n.stop()};t.exports=i},{"../define":41}],13:[function(e,t,r){"use strict";var n=e("../util"),i={classes:function(e){e=e.match(/\S+/g)||[];for(var t=this,r=[],i={},a=0;a0&&this.spawn(r).updateStyle().trigger("class"),t},addClass:function(e){return this.toggleClass(e,!0)},hasClass:function(e){var t=this[0];return null!=t&&t._private.classes[e]?!0:!1},toggleClass:function(e,t){for(var r=e.match(/\S+/g)||[],n=this,i=[],a=0,o=n.length;o>a;a++)for(var s=n[a],l=!1,u=0;u0&&this.spawn(i).updateStyle().trigger("class"),n},removeClass:function(e){return this.toggleClass(e,!1)},flashClass:function(e,t){var r=this;if(null==t)t=250;else if(0===t)return r;return r.addClass(e),setTimeout(function(){r.removeClass(e)},t),r}};t.exports=i},{"../util":94}],14:[function(e,t,r){"use strict";var n={allAre:function(e){return this.filter(e).length===this.length},is:function(e){return this.filter(e).length>0},some:function(e,t){for(var r=0;r0},allAreNeighbors:function(e){return e=this.cy().collection(e),this.neighborhood().intersect(e).length===e.length}};n.allAreNeighbours=n.allAreNeighbors,t.exports=n},{}],15:[function(e,t,r){"use strict";var n={parent:function(e){for(var t=[],r=this._private.cy,n=0;n0&&t.push(a)}return this.spawn(t,{unique:!0}).filter(e)},parents:function(e){for(var t=[],r=this.parent();r.nonempty();){for(var n=0;ne}),maxDegree:i("degree",function(e,t){return e>t}),minIndegree:i("indegree",function(e,t){return t>e}),maxIndegree:i("indegree",function(e,t){return e>t}),minOutdegree:i("outdegree",function(e,t){return t>e}),maxOutdegree:i("outdegree",function(e,t){return e>t})}),a.extend(o,{totalDegree:function(e){for(var t=0,r=this.nodes(),n=0;n0?this.add(s):this;t?l.trigger("position"):l.rtrigger("position")}return this},silentPositions:function(e){return this.positions(e,!0)},renderedPosition:function(e,t){var r=this[0],n=this.cy(),i=n.zoom(),a=n.pan(),s=o.plainObject(e)?e:void 0,l=void 0!==s||void 0!==t&&o.string(e);if(r&&r.isNode()){if(!l){var u=r._private.position;return s={x:u.x*i+a.x,y:u.y*i+a.y},void 0===e?s:s[e]}for(var c=0;c0,d=c;c&&(u=u[0]);var h=d?u._private.position:{x:0,y:0};return i={x:l.x-h.x,y:l.y-h.y},void 0===e?i:i[e]}for(var p=0;p0,d=c;c&&(u=u[0]);var h=d?u._private.position:{x:0,y:0};void 0!==t?r._private.position[e]=t+h[e]:void 0!==i&&(r._private.position={x:i.x+h.x,y:i.y+h.y})}this.rtrigger("position")}else if(!a)return void 0;return this},renderedBoundingBox:function(e){var t=this.boundingBox(e),r=this.cy(),n=r.zoom(),i=r.pan(),a=t.x1*n+i.x,o=t.x2*n+i.x,s=t.y1*n+i.y,l=t.y2*n+i.y;return{x1:a,x2:o,y1:s,y2:l,w:o-a,h:l-s}},updateCompoundBounds:function(){function e(e){var t=e.children(),n=e._private.style,i="include"===n["compound-sizing-wrt-labels"].value,a=t.boundingBox({includeLabels:i,includeEdges:!0}),o={top:n["padding-top"].pfValue,bottom:n["padding-bottom"].pfValue,left:n["padding-left"].pfValue,right:n["padding-right"].pfValue},s=e._private.position,l=!1;"auto"===n.width.value&&(e._private.autoWidth=a.w,s.x=(a.x1+a.x2-o.left+o.right)/2,l=!0),"auto"===n.height.value&&(e._private.autoHeight=a.h,s.y=(a.y1+a.y2-o.top+o.bottom)/2,l=!0),l&&r.push(e)}var t=this.cy();if(!t.styleEnabled()||!t.hasCompoundNodes())return t.collection();for(var r=[],n=this.parent();n.nonempty();){for(var i=0;iv?v:u,c=f>c?f:c,d=d>g?g:d,h=y>h?y:h}else if(x.isEdge()&&o){S=!0;var M=w.source,B=M._private,z=B.position,O=w.target,I=O._private,L=I.position,A=w.rstyle||{},T=0,R=0;if(i&&(T=_.width.pfValue,R=T/2),v=z.x,f=L.x,g=z.y,y=L.y,v>f){var V=v;v=f,f=V}if(g>y){var V=g;g=y,y=V}if(v-=R,f+=R,g-=R,y+=R,u=u>v?v:u,c=f>c?f:c,d=d>g?g:d,h=y>h?y:h,i)for(var F=A.bezierPts||A.linePts||[],j=0;jv?v:u,c=f>c?f:c,d=d>g?g:d,h=y>h?y:h}if(i&&"haystack"===_["curve-style"].strValue){var X=A.haystackPts;if(v=X[0].x,g=X[0].y,f=X[1].x,y=X[1].y,v>f){var V=v;v=f,f=V}if(g>y){var V=g;g=y,y=V}u=u>v?v:u,c=f>c?f:c,d=d>g?g:d,h=y>h?y:h}}if(i){var w=x._private,_=w.style,A=w.rstyle,Y=_.label.strValue,$=_["font-size"],H=_["text-halign"],W=_["text-valign"],Z=A.labelWidth,U=A.labelHeight,G=A.labelX,K=A.labelY,J=x.isEdge(),Q="autorotate"===_["edge-text-rotation"].strValue;if(l&&Y&&$&&null!=U&&null!=Z&&null!=G&&null!=K&&H&&W){var ee,te,re,ne,ie=U,ae=Z;if(J){if(ee=G-ae/2,te=G+ae/2,re=K-ie/2,ne=K+ie/2,Q){var oe=w.rscratch.labelAngle,se=Math.cos(oe),le=Math.sin(oe),ue=function(e,t){return e-=G,t-=K,{x:e*se-t*le+G,y:e*le+t*se+K}},ce=ue(ee,re),de=ue(ee,ne),he=ue(te,re),pe=ue(te,ne);ee=Math.min(ce.x,de.x,he.x,pe.x),te=Math.max(ce.x,de.x,he.x,pe.x),re=Math.min(ce.y,de.y,he.y,pe.y),ne=Math.max(ce.y,de.y,he.y,pe.y)}}else{switch(H.value){case"left":ee=G-ae,te=G;break;case"center":ee=G-ae/2,te=G+ae/2;break;case"right":ee=G,te=G+ae}switch(W.value){case"top":re=K-ie,ne=K;break;case"center":re=K-ie/2,ne=K+ie/2;break;case"bottom":re=K,ne=K+ie}}u=u>ee?ee:u,c=te>c?te:c,d=d>re?re:d,h=ne>h?ne:h}}}}var ve=function(e){return e===1/0||e===-(1/0)?0:e};return u=ve(u),c=ve(c),d=ve(d),h=ve(h),{x1:u,x2:c,y1:d,y2:h,w:c-u,h:h-d}}};var l=function(e){e.uppercaseName=s.capitalize(e.name),e.autoName="auto"+e.uppercaseName,e.labelName="label"+e.uppercaseName,e.outerName="outer"+e.uppercaseName,e.uppercaseOuterName=s.capitalize(e.outerName),n[e.name]=function(){var t=this[0],r=t._private,n=r.cy,i=n._private.styleEnabled;if(t){if(!i)return 1;var a=r.style[e.name];switch(a.strValue){case"auto":return r[e.autoName]||0;case"label":return r.rstyle[e.labelName]||0;default:return a.pfValue}}},n["outer"+e.uppercaseName]=function(){var t=this[0],r=t._private,n=r.cy,i=n._private.styleEnabled;if(t){if(i){var a=r.style,o=t[e.name](),s=a["border-width"].pfValue,l=a[e.paddings[0]].pfValue+a[e.paddings[1]].pfValue;return o+s+l}return 1}},n["rendered"+e.uppercaseName]=function(){var t=this[0];if(t){var r=t[e.name]();return r*this.cy().zoom()}},n["rendered"+e.uppercaseOuterName]=function(){var t=this[0];if(t){var r=t[e.outerName]();return r*this.cy().zoom()}}};l({name:"width",paddings:["padding-left","padding-right"]}),l({name:"height",paddings:["padding-top","padding-bottom"]}),n.modelPosition=n.point=n.position,n.modelPositions=n.points=n.positions,n.renderedPoint=n.renderedPosition,n.relativePoint=n.relativePosition,n.boundingbox=n.boundingBox,n.renderedBoundingbox=n.renderedBoundingBox,t.exports=i},{"../define":41,"../is":77,"../util":94}],19:[function(e,t,r){"use strict";var n=e("../util"),i=e("../is"),a=function(e,t,r){if(!(this instanceof a))return new a(e,t,r);var o=this;if(r=void 0===r||r?!0:!1,void 0===e||void 0===t||!i.core(e))return void n.error("An element must have a core reference and parameters set");var s=t.group; -if(null==s&&(s=null!=t.data.source&&null!=t.data.target?"edges":"nodes"),"nodes"!==s&&"edges"!==s)return void n.error("An element must be of type `nodes` or `edges`; you specified `"+s+"`");if(this.length=1,this[0]=this,this._private={cy:e,single:!0,data:t.data||{},position:t.position||{},autoWidth:void 0,autoHeight:void 0,listeners:[],group:s,style:{},rstyle:{},styleCxts:[],removed:!0,selected:t.selected?!0:!1,selectable:void 0===t.selectable?!0:t.selectable?!0:!1,locked:t.locked?!0:!1,grabbed:!1,grabbable:void 0===t.grabbable?!0:t.grabbable?!0:!1,active:!1,classes:{},animation:{current:[],queue:[]},rscratch:{},scratch:t.scratch||{},edges:[],children:[]},t.renderedPosition){var l=t.renderedPosition,u=e.pan(),c=e.zoom();this._private.position={x:(l.x-u.x)/c,y:(l.y-u.y)/c}}if(i.string(t.classes))for(var d=t.classes.split(/\s+/),h=0,p=d.length;p>h;h++){var v=d[h];v&&""!==v&&(o._private.classes[v]=!0)}(t.style||t.css)&&e.style().applyBypass(this,t.style||t.css),(void 0===r||r)&&this.restore()};t.exports=a},{"../is":77,"../util":94}],20:[function(e,t,r){"use strict";var n=e("../define"),i={on:n.on(),one:n.on({unbindSelfOnTrigger:!0}),once:n.on({unbindAllBindersOnTrigger:!0}),off:n.off(),trigger:n.trigger(),rtrigger:function(e,t){return 0!==this.length?(this.cy().notify({type:e,collection:this}),this.trigger(e,t),this):void 0}};n.eventAliasesOn(i),t.exports=i},{"../define":41}],21:[function(e,t,r){"use strict";var n=e("../is"),i=e("../selector"),a={nodes:function(e){return this.filter(function(e,t){return t.isNode()}).filter(e)},edges:function(e){return this.filter(function(e,t){return t.isEdge()}).filter(e)},filter:function(e){if(n.fn(e)){for(var t=[],r=0;r1&&!i){var a=this.length-1,o=this[a];this[a]=void 0,this[n]=o,t.indexes[o.id()]=n}return this.length--,this},unmerge:function(e){var t=this._private.cy;if(!e)return this;if(n.string(e)){var r=e;e=t.elements(r)}for(var i=0;in&&(n=s,r=o)}return{value:n,ele:r}},min:function(e,t){for(var r,n=1/0,i=this,a=0;as&&(n=s,r=o)}return{value:n,ele:r}}},o=a;o.u=o["|"]=o["+"]=o.union=o.or=o.add,o["\\"]=o["!"]=o["-"]=o.difference=o.relativeComplement=o.subtract=o.not,o.n=o["&"]=o["."]=o.and=o.intersection=o.intersect,o["^"]=o["(+)"]=o["(-)"]=o.symmetricDifference=o.symdiff=o.xor,o.fnFilter=o.filterFn=o.stdFilter,o.complement=o.abscomp=o.absoluteComplement,t.exports=a},{"../is":77,"../selector":81}],22:[function(e,t,r){"use strict";var n={isNode:function(){return"nodes"===this.group()},isEdge:function(){return"edges"===this.group()},isLoop:function(){return this.isEdge()&&this.source().id()===this.target().id()},isSimple:function(){return this.isEdge()&&this.source().id()!==this.target().id()},group:function(){var e=this[0];return e?e._private.group:void 0}};t.exports=n},{}],23:[function(e,t,r){"use strict";var n=e("../util"),i=e("../is"),a=e("./element"),o={prefix:"ele",id:0,generate:function(e,t,r){var n=(i.element(t)?t._private:t,null!=r?r:this.prefix+this.id);if(e.getElementById(n).empty())this.id++;else for(;!e.getElementById(n).empty();)n=this.prefix+ ++this.id;return n}},s=function(e,t,r){if(!(this instanceof s))return new s(e,t,r);if(void 0===e||!i.core(e))return void n.error("A collection must have a reference to the core");var l={},u={},c=!1;if(t){if(t.length>0&&i.plainObject(t[0])&&!i.element(t[0])){c=!0;for(var d=[],h={},p=0,v=t.length;v>p;p++){var f=t[p];null==f.data&&(f.data={});var g=f.data;if(null==g.id)g.id=o.generate(e,f);else if(0!==e.getElementById(g.id).length||h[g.id])continue;var y=new a(e,f,!1);d.push(y),h[g.id]=!0}t=d}}else t=[];this.length=0;for(var p=0,v=t.length;v>p;p++){var m=t[p];if(m){var b=m._private.data.id;(!r||r.unique&&!l[b])&&(l[b]=m,u[b]=this.length,this[this.length]=m,this.length++)}}this._private={cy:e,ids:l,indexes:u},c&&this.restore()},l=a.prototype=s.prototype;l.instanceString=function(){return"collection"},l.spawn=function(e,t,r){return i.core(e)||(r=t,t=e,e=this.cy()),new s(e,t,r)},l.cy=function(){return this._private.cy},l.element=function(){return this[0]},l.collection=function(){return i.collection(this)?this:new s(this._private.cy,[this])},l.unique=function(){return new s(this._private.cy,this,{unique:!0})},l.getElementById=function(e){var t=this._private.cy,r=this._private.ids[e];return r?r:new s(t)},l.json=function(e){var t=this.element(),r=this.cy();if(null==t&&e)return this;if(null==t)return void 0;var a=t._private;if(i.plainObject(e)){r.startBatch(),e.data&&t.data(e.data),e.position&&t.position(e.position);var o=function(r,n,i){var o=e[r];null!=o&&o!==a[r]&&(o?t[n]():t[i]())};return o("removed","remove","restore"),o("selected","select","unselect"),o("selectable","selectify","unselectify"),o("locked","lock","unlock"),o("grabbable","grabify","ungrabify"),null!=e.classes&&t.classes(e.classes),r.endBatch(),this}if(void 0===e){var s={data:n.copy(a.data),position:n.copy(a.position),group:a.group,removed:a.removed,selected:a.selected,selectable:a.selectable,locked:a.locked,grabbable:a.grabbable,classes:null},l=[];for(var u in a.classes)a.classes[u]&&l.push(u);return s.classes=l.join(" "),s}},l.jsons=function(){for(var e=[],t=0;tp;p++){var f=t[p];f.isNode()?(u.push(f),d++):(c.push(f),h++)}l=u.concat(c);for(var p=0,v=l.length;v>p;p++){var f=l[p];if(f.removed()){var g=f._private,y=g.data;if(void 0===y.id)y.id=o.generate(a,f);else if(i.number(y.id))y.id=""+y.id;else{if(i.emptyString(y.id)||!i.string(y.id)){n.error("Can not create element with invalid string ID `"+y.id+"`");continue}if(0!==a.getElementById(y.id).length){n.error("Can not create second element with ID `"+y.id+"`");continue}}var m=y.id;if(f.isNode()){var b=f,x=g.position;null==x.x&&(x.x=0),null==x.y&&(x.y=0)}if(f.isEdge()){for(var w=f,_=["source","target"],E=_.length,D=!1,S=0;E>S;S++){var k=_[S],T=y[k];i.number(T)&&(T=y[k]=""+y[k]),null==T||""===T?(n.error("Can not create edge `"+m+"` with unspecified "+k),D=!0):a.getElementById(T).empty()&&(n.error("Can not create edge `"+m+"` with nonexistant "+k+" `"+T+"`"),D=!0)}if(D)continue;var P=a.getElementById(y.source),C=a.getElementById(y.target);P._private.edges.push(w),C._private.edges.push(w),w._private.source=P,w._private.target=C}g.ids={},g.ids[m]=f,g.removed=!1,a.addToPool(f),r.push(f)}}for(var p=0;d>p;p++){var b=l[p],y=b._private.data;i.number(y.parent)&&(y.parent=""+y.parent);var N=y.parent,M=null!=N;if(M){var B=a.getElementById(N);if(B.empty())y.parent=void 0;else{for(var z=!1,O=B;!O.empty();){if(b.same(O)){z=!0,y.parent=void 0;break}O=O.parent()}z||(B[0]._private.children.push(b),b._private.parent=B[0],a._private.hasCompoundNodes=!0)}}}if(r=new s(a,r),r.length>0){var I=r.add(r.connectedNodes()).add(r.parent());I.updateStyle(e),e?r.rtrigger("add"):r.trigger("add")}return t},l.removed=function(){var e=this[0];return e&&e._private.removed},l.inside=function(){var e=this[0];return e&&!e._private.removed},l.remove=function(e){function t(e){for(var t=e._private.edges,r=0;rh;h++){var v=o[h];n(v)}for(var h=0;h0&&(e&&this.cy().notify({type:"remove",collection:b}),b.trigger("remove"));for(var x={},h=0;h0,a=t.getElementById(n).length>0;if(i||a){var o=this.jsons();this.remove();for(var s=0;s0;if(c){var o=this.jsons(),d=this.descendants(),h=d.merge(d.add(this).connectedEdges());this.remove();for(var s=0;se&&(e=n+e),0>t&&(t=n+t);for(var i=e;i>=0&&t>i&&n>i;i++)r.push(this[i]);return this.spawn(r)},size:function(){return this.length},eq:function(e){return this[e]||this.spawn()},first:function(){return this[0]||this.spawn()},last:function(){return this[this.length-1]||this.spawn()},empty:function(){return 0===this.length},nonempty:function(){return!this.empty()},sort:function(e){if(!n.fn(e))return this;var t=this.toArray().sort(e);return this.spawn(t)},sortByZIndex:function(){return this.sort(i)},zDepth:function(){var e=this[0];if(!e)return void 0;var t=e._private,r=t.group;if("nodes"===r){var n=t.data.parent?e.parents().size():0;return e.isParent()?n:Number.MAX_VALUE}var i=t.source,a=t.target,o=i.zDepth(),s=a.zDepth();return Math.max(o,s,0)}};t.exports=a},{"../is":77,"./zsort":29}],25:[function(e,t,r){"use strict";var n=e("../is"),i=e("../util"),a={layoutPositions:function(e,t,r){var i=this.nodes(),a=this.cy();if(e.trigger({type:"layoutstart",layout:e}),e.animations=[],t.animate){for(var o=0;o0?this.add(i):this;return e?a.rtrigger("style"):a.trigger("style"),this},updateMappers:function(e){var t=this._private.cy,r=t.style();if(e=e||void 0===e?!0:!1,!t.styleEnabled())return this;r.updateMappers(this);var n=this.updateCompoundBounds(),i=n.length>0?this.add(n):this;return e?i.rtrigger("style"):i.trigger("style"),this},renderedCss:function(e){var t=this.cy();if(!t.styleEnabled())return this;var r=this[0];if(r){var n=r.cy().style().getRenderedStyle(r);return void 0===e?n:n[e]}},css:function(e,t){var r=this.cy();if(!r.styleEnabled())return this;var i=!1,a=r.style();if(n.plainObject(e)){var o=e;a.applyBypass(this,o,i);var s=this.updateCompoundBounds(),l=s.length>0?this.add(s):this;l.rtrigger("style")}else if(n.string(e)){if(void 0===t){var u=this[0];return u?a.getStylePropertyValue(u,e):void 0}a.applyBypass(this,e,t,i);var s=this.updateCompoundBounds(),l=s.length>0?this.add(s):this;l.rtrigger("style")}else if(void 0===e){var u=this[0];return u?a.getRawStyle(u):void 0}return this},removeCss:function(e){var t=this.cy();if(!t.styleEnabled())return this;var r=!1,n=t.style(),i=this;if(void 0===e)for(var a=0;a0?this.add(s):this;return l.rtrigger("style"),this},show:function(){return this.css("display","element"),this},hide:function(){return this.css("display","none"),this},visible:function(){var e=this.cy();if(!e.styleEnabled())return!0;var t=this[0],r=e.hasCompoundNodes();if(t){var n=t._private.style;if("visible"!==n.visibility.value||"element"!==n.display.value)return!1;if("nodes"===t._private.group){if(!r)return!0;var i=t._private.data.parent?t.parents():null;if(i)for(var a=0;a0;a||r.push(i)}}return this.spawn(r,{unique:!0}).filter(e)},leaves:function(e){for(var t=this,r=[],n=0;n0;a||r.push(i)}}return this.spawn(r,{unique:!0}).filter(e)},outgoers:function(e){for(var t=this,r=[],n=0;n0&&t.push(c[0]),t.push(s[0])}return this.spawn(t,{unique:!0}).filter(e)},closedNeighborhood:function(e){return this.neighborhood().add(this).filter(e)},openNeighborhood:function(e){return this.neighborhood(e)}}),l.neighbourhood=l.neighborhood,l.closedNeighbourhood=l.closedNeighborhood,l.openNeighbourhood=l.openNeighborhood,o.extend(l,{source:function(e){var t,r=this[0];return r&&(t=r._private.source),t&&e?t.filter(e):t},target:function(e){var t,r=this[0];return r&&(t=r._private.target),t&&e?t.filter(e):t},sources:n({attr:"source"}),targets:n({attr:"target"})}),o.extend(l,{edgesWith:i(),edgesTo:i({thisIs:"source"})}),o.extend(l,{connectedEdges:function(e){for(var t=[],r=this,n=0;n0);return n.map(function(e){return e.closedNeighborhood()})}}),t.exports=l},{"../is":77,"../util":94}],29:[function(e,t,r){"use strict";var n=function(e,t){var r=e.cy(),n=e._private,i=t._private,a=n.style["z-index"].value-i.style["z-index"].value,o=0,s=0,l=r.hasCompoundNodes(),u="nodes"===n.group,c="edges"===n.group,d="nodes"===i.group,h="edges"===i.group;l&&(o=e.zDepth(),s=t.zDepth());var p=o-s,v=0===p;return v?u&&h?1:c&&d?-1:0===a?n.index-i.index:a:p};t.exports=n},{}],30:[function(e,t,r){"use strict";var n=e("../is"),i=e("../util"),a=e("../collection"),o=e("../collection/element"),s=e("../window"),l=(s?s.document:null,e("../extensions/renderer/null"),{add:function(e){var t,r=this;if(n.elementOrCollection(e)){var s=e;if(s._private.cy===r)t=s.restore();else{for(var l=[],u=0;uu;u++){var v=h[u],f=d[v];if(n.array(f))for(var g=0,y=f.length;y>g;g++){var m=i.extend({group:v},f[g]);l.push(m)}}t=new a(r,l)}else{var m=e;t=new o(r,m).collection()}return t},remove:function(e){if(n.elementOrCollection(e))e=e;else if(n.string(e)){var t=e;e=this.$(t)}return e.remove()},load:function(e,t,r){var a=this;a.notifications(!1);var o=a.elements();o.length>0&&o.remove(),null!=e&&(n.plainObject(e)||n.array(e))&&a.add(e),a.one("layoutready",function(e){a.notifications(!0),a.trigger(e),a.notify({type:"load",collection:a.elements()}),a.one("load",t),a.trigger("load")}).one("layoutstop",function(){a.one("done",r),a.trigger("done")});var s=i.extend({},a._private.options.layout);return s.eles=a.$(),a.layout(s),this}});t.exports=l},{"../collection":23,"../collection/element":19,"../extensions/renderer/null":73,"../is":77,"../util":94,"../window":100}],31:[function(e,t,r){"use strict";var n=e("../define"),i=e("../util"),a=e("../is"),o={animate:n.animate(),animation:n.animation(),animated:n.animated(),clearQueue:n.clearQueue(),delay:n.delay(),delayAnimation:n.delayAnimation(),stop:n.stop(),addToAnimationPool:function(e){var t=this;t.styleEnabled()&&t._private.aniEles.merge(e)},stopAnimationLoop:function(){this._private.animationsRunning=!1},startAnimationLoop:function(){function e(){c._private.animationsRunning&&i.requestAnimationFrame(function(r){t(r),e()})}function t(e){function t(t,i){var o=t._private,s=o.animation.current,l=o.animation.queue,u=!1;if(0===s.length){var c=l.shift();c&&s.push(c)}for(var d=function(e){for(var t=e.length-1;t>=0;t--){var r=e[t];r()}e.splice(0,e.length)},h=s.length-1;h>=0;h--){var p=s[h],v=p._private;v.stopped?(s.splice(h,1),v.hooked=!1,v.playing=!1,v.started=!1,d(v.frames)):(v.playing||v.applying)&&(v.playing&&v.applying&&(v.applying=!1),v.started||r(t,p,e),n(t,p,e,i),v.applying&&(v.applying=!1),d(v.frames),p.completed()&&(s.splice(h,1),v.hooked=!1,v.playing=!1,v.started=!1,d(v.completes)),u=!0)}return i||0!==s.length||0!==l.length||a.push(t),u}for(var i=c._private.aniEles,a=[],o=!1,s=0;s0){var p=i.updateCompoundBounds();h=p.length>0?i.add(p):i}c.notify({type:"draw",collection:h})}i.unmerge(a)}function r(e,t,r){var n=a.core(e),i=!n,o=e,s=c._private.style,l=t._private;if(i){var u=o._private.position;l.startPosition=l.startPosition||{x:u.x,y:u.y},l.startStyle=l.startStyle||s.getValueStyle(o)}if(n){var d=c._private.pan;l.startPan=l.startPan||{x:d.x,y:d.y},l.startZoom=null!=l.startZoom?l.startZoom:c._private.zoom}l.started=!0,l.startTime=r-l.progress*l.duration}function n(e,t,r,n){var i=c._private.style,s=!n,l=e._private,d=t._private,p=d.easing,v=d.startTime;if(!d.easingImpl)if(null==p)d.easingImpl=h.linear;else{var f;if(a.string(p)){var g=i.parse("transition-timing-function",p);f=g.value}else f=p;var y,m;a.string(f)?(y=f,m=[]):(y=f[1],m=f.slice(2).map(function(e){return+e})),m.length>0?("spring"===y&&m.push(d.duration),d.easingImpl=h[y].apply(null,m)):d.easingImpl=h[y]}var b,x=d.easingImpl;if(b=0===d.duration?1:(r-v)/d.duration,d.applying&&(b=d.progress),0>b?b=0:b>1&&(b=1),null==d.delay){var w=d.startPosition,_=d.position,E=l.position;_&&s&&(o(w.x,_.x)&&(E.x=u(w.x,_.x,b,x)),o(w.y,_.y)&&(E.y=u(w.y,_.y,b,x)));var D=d.startPan,S=d.pan,k=l.pan,T=null!=S&&n;T&&(o(D.x,S.x)&&(k.x=u(D.x,S.x,b,x)),o(D.y,S.y)&&(k.y=u(D.y,S.y,b,x)),e.trigger("pan"));var P=d.startZoom,C=d.zoom,N=null!=C&&n;N&&(o(P,C)&&(l.zoom=u(P,C,b,x)),e.trigger("zoom")),(T||N)&&e.trigger("viewport");var M=d.style;if(M&&s)for(var B=0;Br?r=0:r>1&&(r=1);var i,o;if(i=null!=e.pfValue||null!=e.value?null!=e.pfValue?e.pfValue:e.value:e,o=null!=t.pfValue||null!=t.value?null!=t.pfValue?t.pfValue:t.value:t,a.number(i)&&a.number(o))return n(i,o,r);if(a.array(i)&&a.array(o)){for(var s=[],l=0;ld&&Math.abs(s.v)>d))break;return a?function(e){return u[e*(u.length-1)|0]}:c}}(),h={linear:function(e,t,r){return e+(t-e)*r},ease:l(.25,.1,.25,1),"ease-in":l(.42,0,1,1),"ease-out":l(0,0,.58,1),"ease-in-out":l(.42,0,.58,1),"ease-in-sine":l(.47,0,.745,.715),"ease-out-sine":l(.39,.575,.565,1),"ease-in-out-sine":l(.445,.05,.55,.95),"ease-in-quad":l(.55,.085,.68,.53),"ease-out-quad":l(.25,.46,.45,.94),"ease-in-out-quad":l(.455,.03,.515,.955),"ease-in-cubic":l(.55,.055,.675,.19),"ease-out-cubic":l(.215,.61,.355,1),"ease-in-out-cubic":l(.645,.045,.355,1),"ease-in-quart":l(.895,.03,.685,.22),"ease-out-quart":l(.165,.84,.44,1),"ease-in-out-quart":l(.77,0,.175,1),"ease-in-quint":l(.755,.05,.855,.06),"ease-out-quint":l(.23,1,.32,1),"ease-in-out-quint":l(.86,0,.07,1),"ease-in-expo":l(.95,.05,.795,.035),"ease-out-expo":l(.19,1,.22,1),"ease-in-out-expo":l(1,0,0,1),"ease-in-circ":l(.6,.04,.98,.335),"ease-out-circ":l(.075,.82,.165,1),"ease-in-out-circ":l(.785,.135,.15,.86),spring:function(e,t,r){var n=d(e,t,r);return function(e,t,r){return e+(t-e)*n(r)}},"cubic-bezier":function(e,t,r,n){return l(e,t,r,n)}}}}};t.exports=o},{"../define":41,"../is":77,"../util":94}],32:[function(e,t,r){"use strict";var n=e("../define"),i={on:n.on(),one:n.on({unbindSelfOnTrigger:!0}),once:n.on({unbindAllBindersOnTrigger:!0}),off:n.off(),trigger:n.trigger()};n.eventAliasesOn(i),t.exports=i},{"../define":41}],33:[function(e,t,r){"use strict";var n={png:function(e){var t=this._private.renderer;return e=e||{},t.png(e)},jpg:function(e){var t=this._private.renderer;return e=e||{},e.bg=e.bg||"#fff",t.jpg(e)}};n.jpeg=n.jpg,t.exports=n},{}],34:[function(e,t,r){"use strict";var n=e("../window"),i=e("../util"),a=e("../collection"),o=e("../is"),s=e("../promise"),l=e("../define"),u=function(e){if(!(this instanceof u))return new u(e);var t=this;e=i.extend({},e);var r=e.container;r&&!o.htmlElement(r)&&o.htmlElement(r[0])&&(r=r[0]);var l=r?r._cyreg:null;l=l||{},l&&l.cy&&(l.cy.destroy(),l={});var c=l.readies=l.readies||[];r&&(r._cyreg=l),l.cy=t;var d=void 0!==n&&void 0!==r&&!e.headless,h=e;h.layout=i.extend({name:d?"grid":"null"},h.layout),h.renderer=i.extend({ -name:d?"canvas":"null"},h.renderer);var p=function(e,t,r){return void 0!==t?t:void 0!==r?r:e},v=this._private={container:r,ready:!1,initrender:!1,options:h,elements:[],id2index:{},listeners:[],onRenders:[],aniEles:a(this),scratch:{},layout:null,renderer:null,notificationsEnabled:!0,minZoom:1e-50,maxZoom:1e50,zoomingEnabled:p(!0,h.zoomingEnabled),userZoomingEnabled:p(!0,h.userZoomingEnabled),panningEnabled:p(!0,h.panningEnabled),userPanningEnabled:p(!0,h.userPanningEnabled),boxSelectionEnabled:p(!0,h.boxSelectionEnabled),autolock:p(!1,h.autolock,h.autolockNodes),autoungrabify:p(!1,h.autoungrabify,h.autoungrabifyNodes),autounselectify:p(!1,h.autounselectify),styleEnabled:void 0===h.styleEnabled?d:h.styleEnabled,zoom:o.number(h.zoom)?h.zoom:1,pan:{x:o.plainObject(h.pan)&&o.number(h.pan.x)?h.pan.x:0,y:o.plainObject(h.pan)&&o.number(h.pan.y)?h.pan.y:0},animation:{current:[],queue:[]},hasCompoundNodes:!1,deferredExecQueue:[]},f=h.selectionType;void 0===f||"additive"!==f&&"single"!==f?v.selectionType="single":v.selectionType=f,o.number(h.minZoom)&&o.number(h.maxZoom)&&h.minZoom0?h.wheelSensitivity:1,motionBlur:void 0===h.motionBlur?!0:h.motionBlur,motionBlurOpacity:void 0===h.motionBlurOpacity?.05:h.motionBlurOpacity,pixelRatio:o.number(h.pixelRatio)&&h.pixelRatio>0?h.pixelRatio:"auto"===h.pixelRatio?void 0:1,desktopTapThreshold:void 0===h.desktopTapThreshold?4:h.desktopTapThreshold,touchTapThreshold:void 0===h.touchTapThreshold?8:h.touchTapThreshold},h.renderer));var y=[h.style,h.elements];g(function(e){var r=e[0],n=e[1];v.styleEnabled&&t.setStyle(r),h.initrender&&(t.on("initrender",h.initrender),t.on("initrender",function(){v.initrender=!0})),t.load(n,function(){t.startAnimationLoop(),v.ready=!0,o.fn(h.ready)&&t.on("ready",h.ready);for(var e=0;e0;)t.removeChild(t.childNodes[0]);return e},getElementById:function(e){var t=this._private.id2index[e];return void 0!==t?this._private.elements[t]:a(this)},selectionType:function(){return this._private.selectionType},hasCompoundNodes:function(){return this._private.hasCompoundNodes},styleEnabled:function(){return this._private.styleEnabled},addToPool:function(e){for(var t=this._private.elements,r=this._private.id2index,n=0;n0&&l>0&&!isNaN(r.w)&&!isNaN(r.h)&&r.w>0&&r.h>0){o=Math.min((s-2*t)/r.w,(l-2*t)/r.h),o=o>this._private.maxZoom?this._private.maxZoom:o,o=othis._private.maxZoom?this._private.maxZoom:r,r=rt.maxZoom||!t.zoomingEnabled?o=!0:(t.zoom=l,a.push("zoom"))}if(i&&(!o||!e.cancelOnFailedZoom)&&t.panningEnabled){var u=e.pan;n.number(u.x)&&(t.pan.x=u.x,s=!1),n.number(u.y)&&(t.pan.y=u.y,s=!1),s||a.push("pan")}return a.length>0&&(a.push("viewport"),this.trigger(a.join(" ")),this.notify({type:"viewport"})),this},center:function(e){var t=this.getCenterPan(e);return t&&(this._private.pan=t,this.trigger("pan viewport"),this.notify({type:"viewport"})),this},getCenterPan:function(e,t){if(this._private.panningEnabled){if(n.string(e)){var r=e;e=this.elements(r)}else n.elementOrCollection(e)||(e=this.elements());var i=e.boundingBox(),a=this.width(),o=this.height();t=void 0===t?this._private.zoom:t;var s={x:(a-t*(i.x1+i.x2))/2,y:(o-t*(i.y1+i.y2))/2};return s}},reset:function(){return this._private.panningEnabled&&this._private.zoomingEnabled?(this.viewport({pan:{x:0,y:0},zoom:1}),this):this},width:function(){var e=this._private.container;return e?e.clientWidth:1},height:function(){var e=this._private.container;return e?e.clientHeight:1},extent:function(){var e=this._private.pan,t=this._private.zoom,r=this.renderedExtent(),n={x1:(r.x1-e.x)/t,x2:(r.x2-e.x)/t,y1:(r.y1-e.y)/t,y2:(r.y2-e.y)/t};return n.w=n.x2-n.x1,n.h=n.y2-n.y1,n},renderedExtent:function(){var e=this.width(),t=this.height();return{x1:0,y1:0,x2:e,y2:t,w:e,h:t}}};i.centre=i.center,i.autolockNodes=i.autolock,i.autoungrabifyNodes=i.autoungrabify,t.exports=i},{"../is":77}],41:[function(e,t,r){"use strict";var n=e("./util"),i=e("./is"),a=e("./selector"),o=e("./promise"),s=e("./event"),l=e("./animation"),u={data:function(e){var t={field:"data",bindingEvent:"data",allowBinding:!1,allowSetting:!1,allowGetting:!1,settingEvent:"data",settingTriggersEvent:!1,triggerFnName:"trigger",immutableKeys:{},updateStyle:!1,onSet:function(e){},canSet:function(e){return!0}};return e=n.extend({},t,e),function(t,r){var n=e,a=this,o=void 0!==a.length,s=o?a:[a],l=o?a[0]:a;if(i.string(t)){if(n.allowGetting&&void 0===r){var u;return l&&(u=l._private[n.field][t]),u}if(n.allowSetting&&void 0!==r){var c=!n.immutableKeys[t];if(c){for(var d=0,h=s.length;h>d;d++)n.canSet(s[d])&&(s[d]._private[n.field][t]=r);n.updateStyle&&a.updateStyle(),n.onSet(a),n.settingTriggersEvent&&a[n.triggerFnName](n.settingEvent)}}}else if(n.allowSetting&&i.plainObject(t)){var p,v,f=t;for(p in f){v=f[p];var c=!n.immutableKeys[p];if(c)for(var d=0,h=s.length;h>d;d++)n.canSet(s[d])&&(s[d]._private[n.field][p]=v)}n.updateStyle&&a.updateStyle(),n.onSet(a),n.settingTriggersEvent&&a[n.triggerFnName](n.settingEvent)}else if(n.allowBinding&&i.fn(t)){var g=t;a.bind(n.bindingEvent,g)}else if(n.allowGetting&&void 0===t){var u;return l&&(u=l._private[n.field]),u}return a}},removeData:function(e){var t={field:"data",event:"data",triggerFnName:"trigger",triggerEvent:!1,immutableKeys:{}};return e=n.extend({},t,e),function(t){var r=e,n=this,a=void 0!==n.length,o=a?n:[n];if(i.string(t)){for(var s=t.split(/\s+/),l=s.length,u=0;l>u;u++){var c=s[u];if(!i.emptyString(c)){var d=!r.immutableKeys[c];if(d)for(var h=0,p=o.length;p>h;h++)o[h]._private[r.field][c]=void 0}}r.triggerEvent&&n[r.triggerFnName](r.event)}else if(void 0===t){for(var h=0,p=o.length;p>h;h++){var v=o[h]._private[r.field];for(var c in v){var f=!r.immutableKeys[c];f&&(v[c]=void 0)}}r.triggerEvent&&n[r.triggerFnName](r.event)}return n}},event:{regex:/(\w+)(\.\w+)?/,optionalTypeRegex:/(\w+)?(\.\w+)?/,falseCallback:function(){return!1}},on:function(e){var t={unbindSelfOnTrigger:!1,unbindAllBindersOnTrigger:!1};return e=n.extend({},t,e),function(t,r,n,o){var s=this,l=void 0!==s.length,c=l?s:[s],d=i.string(t),h=e;if(i.plainObject(r)?(o=n,n=r,r=void 0):(i.fn(r)||r===!1)&&(o=r,n=void 0,r=void 0),(i.fn(n)||n===!1)&&(o=n,n=void 0),!i.fn(o)&&o!==!1&&d)return s;if(d){var p={};p[t]=o,t=p}for(var v in t)if(o=t[v],o===!1&&(o=u.event.falseCallback),i.fn(o)){v=v.split(/\s+/);for(var f=0;f0:void 0}},clearQueue:function(e){var t={};return e=n.extend({},t,e),function(){var e=this,t=void 0!==e.length,r=t?e:[e],n=this._private.cy||this;if(!n.styleEnabled())return this;for(var i=0;i0;){var g=n.collection();i.bfs({roots:f[0],visit:function(e,t,r,n,i){g=g.add(r)},directed:!1}),f=f.not(g),v.push(g)}e=n.collection();for(var d=0;dP;){for(var C=k.shift(),N=C.neighborhood().nodes(),M=!1,d=0;dd;d++)for(var B=x[d],R=B.length,V=0;R>V;V++){var p=B[V],F=p._private.scratch.breadthfirst,j=O(p);j&&(F.intEle=j,A.push(p))}for(var d=0;dx.length-1;)x.push([]);x[X].push(p),F.depth=X,F.index=x[X].length-1}z()}var Y=0;if(r.avoidOverlap){for(var d=0;du||0===t)&&(n+=l/c,i++)}return i=Math.max(1,i),n/=i,0===i&&(n=void 0),U[e.id()]=n,n},K=function(e,t){var r=G(e),n=G(t);return r-n},J=0;3>J;J++){for(var d=0;d0&&x[0].length<=3?c/2:0),h=2*Math.PI/x[i].length*a;return 0===i&&1===x[0].length&&(d=1),{x:ee.x+d*Math.cos(h),y:ee.y+d*Math.sin(h)}}return{x:ee.x+(a+1-(o+1)/2)*s,y:(i+1)*l}}var p={x:ee.x+(a+1-(o+1)/2)*s,y:(i+1)*l};return t?p:p},re={},d=x.length-1;d>=0;d--)for(var B=x[d],V=0;V1&&t.avoidOverlap){p*=1.75;var b=Math.cos(h)-Math.cos(0),x=Math.sin(h)-Math.sin(0),w=Math.sqrt(p*p/(b*b+x*x));l=Math.max(w,l)}var _=function(e,r){var n=t.startAngle+e*h*(i?1:-1),a=l*Math.cos(n),o=l*Math.sin(n),s={ -x:c.x+a,y:c.y+o};return s};return s.layoutPositions(this,t,_),this},t.exports=n},{"../../is":77,"../../math":79,"../../util":94}],47:[function(e,t,r){"use strict";function n(e){this.options=i.extend({},o,e)}var i=e("../../util"),a=e("../../math"),o={fit:!0,padding:30,startAngle:1.5*Math.PI,sweep:void 0,clockwise:!0,equidistant:!1,minNodeSpacing:10,boundingBox:void 0,avoidOverlap:!0,height:void 0,width:void 0,concentric:function(e){return e.degree()},levelWidth:function(e){return e.maxDegree()/4},animate:!1,animationDuration:500,animationEasing:void 0,ready:void 0,stop:void 0};n.prototype.run=function(){for(var e=this.options,t=e,r=void 0!==t.counterclockwise?!t.counterclockwise:t.clockwise,n=e.cy,i=t.eles,o=i.nodes().not(":parent"),s=a.makeBoundingBox(t.boundingBox?t.boundingBox:{x1:0,y1:0,w:n.width(),h:n.height()}),l={x:s.x1+s.w/2,y:s.y1+s.h/2},u=[],c=t.startAngle,d=0,h=0;h0){var x=Math.abs(m[0].value-b.value);x>=g&&(m=[],y.push(m))}m.push(b)}var w=d+t.minNodeSpacing;if(!t.avoidOverlap){var _=y.length>0&&y[0].length>1,E=Math.min(s.w,s.h)/2-w,D=E/(y.length+_?1:0);w=Math.min(w,D)}for(var S=0,h=0;h1&&t.avoidOverlap){var C=Math.cos(P)-Math.cos(0),N=Math.sin(P)-Math.sin(0),M=Math.sqrt(w*w/(C*C+N*N));S=Math.max(M,S)}k.r=S,S+=w}if(t.equidistant){for(var B=0,S=0,h=0;ha;a++)for(var o=e.layoutNodes[e.idToIndex[n[a]]],l=a+1;i>l;l++){var u=e.layoutNodes[e.idToIndex[n[l]]];s(o,u,e,t)}},s=function(e,t,r,n){var i=e.cmptId,a=t.cmptId;if(i===a||r.isCompound){var o=t.positionX-e.positionX,s=t.positionY-e.positionY;if(0!==o||0!==s){var c=l(e,t,o,s);if(c>0)var d=n.nodeOverlap*c,h=Math.sqrt(o*o+s*s),p=d*o/h,v=d*s/h;else var f=u(e,o,s),g=u(t,-1*o,-1*s),y=g.x-f.x,m=g.y-f.y,b=y*y+m*m,h=Math.sqrt(b),d=(e.nodeRepulsion+t.nodeRepulsion)/b,p=d*y/h,v=d*m/h;e.isLocked||(e.offsetX-=p,e.offsetY-=v),t.isLocked||(t.offsetX+=p,t.offsetY+=v)}}},l=function(e,t,r,n){if(r>0)var i=e.maxX-t.minX;else var i=t.maxX-e.minX;if(n>0)var a=e.maxY-t.minY;else var a=t.maxY-e.minY;return i>=0&&a>=0?Math.sqrt(i*i+a*a):0},u=function(e,t,r){var n=e.positionX,i=e.positionY,a=e.height||1,o=e.width||1,s=r/t,l=a/o,u={};do{if(0===t&&r>0){u.x=n,u.y=i+a/2;break}if(0===t&&0>r){u.x=n,u.y=i+a/2;break}if(t>0&&s>=-1*l&&l>=s){u.x=n+o/2,u.y=i+o*r/2/t;break}if(0>t&&s>=-1*l&&l>=s){u.x=n-o/2,u.y=i-o*r/2/t;break}if(r>0&&(-1*l>=s||s>=l)){u.x=n+a*t/2/r,u.y=i+a/2;break}if(0>r&&(-1*l>=s||s>=l)){u.x=n-a*t/2/r,u.y=i-a/2;break}}while(!1);return u},c=function(e,t){for(var r=0;rc;c++){var d=e.layoutNodes[e.idToIndex[i[c]]];if(!d.isLocked){var h=o-d.positionX,p=s-d.positionY,v=Math.sqrt(h*h+p*p);if(v>r){var f=t.gravity*h/v,g=t.gravity*p/v;d.offsetX+=f,d.offsetY+=g}}}}},h=function(e,t){var r=[],n=0,i=-1;for(r.push.apply(r,e.graphSet[0]),i+=e.graphSet[0].length;i>=n;){var a=r[n++],o=e.idToIndex[a],s=e.layoutNodes[o],l=s.children;if(0r)var i={x:r*e/n,y:r*t/n};else var i={x:e,y:t};return i},f=function(e,t){var r=e.parentId;if(null!=r){var n=t.layoutNodes[t.idToIndex[r]],i=!1;return(null==n.maxX||e.maxX+n.padRight>n.maxX)&&(n.maxX=e.maxX+n.padRight,i=!0),(null==n.minX||e.minX-n.padLeftn.maxY)&&(n.maxY=e.maxY+n.padBottom,i=!0),(null==n.minY||e.minY-n.padTopy&&(v+=g+t.componentSpacing,p=0,f=0,g=0)}},y=function(e){return i?!1:(a(r,n,e),r.temperature=r.temperature*n.coolingFactor,r.temperature=b;){var E=m[b++],D=a.idToIndex[E],v=a.layoutNodes[D],S=v.children;if(S.length>0){a.graphSet.push(S);for(var c=0;cn.count?0:n.graph},h=function(e,t,r,n){var i=n.graphSet[r];if(-1s){var f=d(),g=h();(f-1)*g>=s?d(f-1):(g-1)*f>=s&&h(g-1)}else for(;s>c*u;){var f=d(),g=h();(g+1)*f>=s?h(g+1):d(f+1)}var y=o.w/c,m=o.h/u;if(t.condense&&(y=0,m=0),t.avoidOverlap)for(var b=0;b=c&&(N=0,C++)},B={},b=0;b=o&&s>=e&&t>=l&&u>=t;return c},o=function(e,t,r,n,i){var a=e*Math.cos(n)-t*Math.sin(n),o=e*Math.sin(n)+t*Math.cos(n),s=a*r,l=o*r,u=s+i.x,c=l+i.y;return{x:u,y:c}},s=function(e,t,r,n){for(var i=[],a=0;a(s=i.sqDistanceToFiniteLine(e,t,E[D],E[D+1],E[D+2],E[D+3]))&&d.push(n);else if("bezier"===h.edgeType||"multibezier"===h.edgeType||"self"===h.edgeType||"compound"===h.edgeType)for(var E=h.allpts,D=0;D+5(s=i.sqDistanceToQuadraticBezier(e,t,E[D],E[D+1],E[D+2],E[D+3],E[D+4],E[D+5]))&&d.push(n);if(w&&_()&&0===d.length||d[d.length-1]!==n)for(var b=b||o.source,x=x||o.target,S=f.width.pfValue,k=l.getArrowWidth(S),T=[{name:"source",x:h.arrowStartX,y:h.arrowStartY,angle:h.srcArrowAngle},{name:"target",x:h.arrowEndX,y:h.arrowEndY,angle:h.tgtArrowAngle},{name:"mid-source",x:h.midX,y:h.midY,angle:h.midsrcArrowAngle},{name:"mid-target",x:h.midX,y:h.midY,angle:h.midtgtArrowAngle}],D=0;D0&&d[d.length-1]===n&&(a(b),a(x))}}function s(r){var n=r._private,a=g;if("no"!==n.style["text-events"].strValue)if("edges"===n.group&&"autorotate"===n.style["edge-text-rotation"].strValue){var o=n.rstyle,s=o.labelWidth+2*a,l=o.labelHeight+2*a,u=o.labelX,c=o.labelY,h=n.rscratch.labelAngle,p=Math.cos(h),v=Math.sin(h),f=function(e,t){return e-=u,t-=c,{x:e*p-t*v+u,y:e*v+t*p+c}},y=u-s/2,m=u+s/2,b=c-l/2,x=c+l/2,w=f(y,b),_=f(y,x),E=f(m,b),D=f(m,x),S=[w.x,w.y,E.x,E.y,D.x,D.y,_.x,_.y];i.pointInsidePolygonPoints(e,t,S)&&d.push(r)}else{var k=r.boundingBox({includeLabels:!0,includeNodes:!1,includeEdges:!1});k.x1-=a,k.y1-=a,k.x2+=a,k.y2+=a,k.w=k.x2-k.x1,k.h=k.y2-k.y1,i.inBoundingBox(k,e,t)&&d.push(r)}}for(var l=this,u=this,c=u.getCachedZSortedEles(),d=[],h=u.cy.zoom(),p=u.cy.hasCompoundNodes(),v=(n?24:8)/h,f=(n?8:2)/h,g=(n?8:2)/h,y=c.length-1;y>=0;y--){var m=c[y],b=m._private;if(d.length>0)break;"nodes"===b.group?a(m):o(m),s(m)}return d.length>0?d[d.length-1]:null},s.getAllInBox=function(e,t,r,n){var a=this.getCachedNodes(),o=this.getCachedEdges(),s=[],l=Math.min(e,r),u=Math.max(e,r),c=Math.min(t,n),d=Math.max(t,n);e=l,r=u,t=c,n=d;for(var h=i.makeBoundingBox({x1:e,y1:t,x2:r,y2:n}),p=0;po){for(var h=u.split(/\s+/),p="",v=0;v=m?p+=f+" ":(s.push(p),p=f+" ")}p.match(/^\s+$/)||s.push(p)}else s.push(u)}i.labelWrapCachedLines=s,i.labelWrapCachedText=r=s.join("\n"),i.labelWrapKey=i.labelKey}return r},s.calculateLabelDimensions=function(e,t,r){var n=this,i=e._private.style,a=i["font-style"].strValue,o=i["font-size"].pfValue+"px",s=i["font-family"].strValue,l=i["font-weight"].strValue,u=e._private.labelKey;r&&(u+="$@$"+r);var c=n.labelDimCache||(n.labelDimCache={});if(c[u])return c[u];var d=this.labelCalcDiv;d||(d=this.labelCalcDiv=document.createElement("div"),document.body.appendChild(d));var h=d.style;return h.fontFamily=s,h.fontStyle=a,h.fontSize=o,h.fontWeight=l,h.position="absolute",h.left="-9999px",h.top="-9999px",h.zIndex="-1",h.visibility="hidden",h.pointerEvents="none",h.padding="0",h.lineHeight="1","wrap"===i["text-wrap"].value?h.whiteSpace="pre":h.whiteSpace="normal",d.textContent=t,c[u]={width:d.clientWidth,height:d.clientHeight},c[u]},s.recalculateRenderedStyle=function(e){for(var t=[],r=[],n={},i=0;ib?b+"$-$"+m:m+"$-$"+b,y&&(t="unbundled$-$"+v.id),null==s[t]&&(s[t]=[],l.push(t)),s[t].push(h),y&&(s[t].hasUnbundled=!0)}else u.push(h)}for(var x,w,_,E,D,S,k,T,P,C,N,M,B,z,O=0;OE.data.id){var L=x;x=w,w=L}if(D=_.position,S=E.position,k=x.outerWidth(),T=x.outerHeight(),P=w.outerWidth(),C=w.outerHeight(),N=r.nodeShapes[this.getNodeShape(x)],M=r.nodeShapes[this.getNodeShape(w)],z=!1,I.length>1&&x!==w||I.hasUnbundled){var A=N.intersectLine(D.x,D.y,k,T,S.x,S.y,0),R=M.intersectLine(S.x,S.y,P,C,D.x,D.y,0),V={x1:A[0],x2:R[0],y1:A[1],y2:R[1]},F=R[1]-A[1],j=R[0]-A[0],q=Math.sqrt(j*j+F*F),X={ -x:j,y:F},Y={x:X.x/q,y:X.y/q};B={x:-Y.y,y:Y.x},(M.checkPoint(A[0],A[1],0,P,C,S.x,S.y)||N.checkPoint(R[0],R[1],0,k,T,D.x,D.y))&&(B={},z=!0)}for(var h,$,H,d=0;dIe;Ie++){var Le=Be[Ie],Ae=ze[Ie],Re=1-Le,Ve=Le,Fe={x:V.x1*Re+V.x2*Ve,y:V.y1*Re+V.y2*Ve};H.segpts.push(Fe.x+B.x*Ae,Fe.y+B.y*Ae)}}else if(I.length%2!==1||d!==Math.floor(I.length/2)||y){var je=y;H.edgeType=je?"multibezier":"bezier",H.ctrlpts=[];for(var qe=0;ee>qe;qe++){var Xe,Ye=(.5-I.length/2+d)*te,$e=i.signum(Ye);je&&(re=J?J.pfValue[qe]:te,ne=Q.value[qe]),Xe=y?re:void 0!==re?$e*re:void 0;var He=void 0!==Xe?Xe:Ye,Re=!ie||y?1-ne:ne,Ve=!ie||y?ne:1-ne,Fe={x:V.x1*Re+V.x2*Ve,y:V.y1*Re+V.y2*Ve};H.ctrlpts.push(Fe.x+B.x*He,Fe.y+B.y*He)}}else H.edgeType="straight";this.findEndpoints(h);var We=!a.number(H.startX)||!a.number(H.startY),Ze=!a.number(H.arrowStartX)||!a.number(H.arrowStartY),Ue=!a.number(H.endX)||!a.number(H.endY),Ge=!a.number(H.arrowEndX)||!a.number(H.arrowEndY),Ke=3,Je=this.getArrowWidth(K.width.pfValue)*this.arrowShapeHeight,Qe=Ke*Je;if("bezier"===H.edgeType){var et=i.distance({x:H.ctrlpts[0],y:H.ctrlpts[1]},{x:H.startX,y:H.startY}),tt=Qe>et,rt=i.distance({x:H.ctrlpts[0],y:H.ctrlpts[1]},{x:H.endX,y:H.endY}),nt=Qe>rt,it=!1;if(We||Ze||tt){it=!0;var at={x:H.ctrlpts[0]-D.x,y:H.ctrlpts[1]-D.y},ot=Math.sqrt(at.x*at.x+at.y*at.y),st={x:at.x/ot,y:at.y/ot},lt=Math.max(k,T),ut={x:H.ctrlpts[0]+2*st.x*lt,y:H.ctrlpts[1]+2*st.y*lt},ct=N.intersectLine(D.x,D.y,k,T,ut.x,ut.y,0);tt?(H.ctrlpts[0]=H.ctrlpts[0]+st.x*(Qe-et),H.ctrlpts[1]=H.ctrlpts[1]+st.y*(Qe-et)):(H.ctrlpts[0]=ct[0]+st.x*Qe,H.ctrlpts[1]=ct[1]+st.y*Qe)}if(Ue||Ge||nt){it=!0;var at={x:H.ctrlpts[0]-S.x,y:H.ctrlpts[1]-S.y},ot=Math.sqrt(at.x*at.x+at.y*at.y),st={x:at.x/ot,y:at.y/ot},lt=Math.max(k,T),ut={x:H.ctrlpts[0]+2*st.x*lt,y:H.ctrlpts[1]+2*st.y*lt},dt=M.intersectLine(S.x,S.y,P,C,ut.x,ut.y,0);nt?(H.ctrlpts[0]=H.ctrlpts[0]+st.x*(Qe-rt),H.ctrlpts[1]=H.ctrlpts[1]+st.y*(Qe-rt)):(H.ctrlpts[0]=dt[0]+st.x*Qe,H.ctrlpts[1]=dt[1]+st.y*Qe)}it&&this.findEndpoints(h)}if("multibezier"===H.edgeType||"bezier"===H.edgeType||"self"===H.edgeType||"compound"===H.edgeType){H.allpts=[],H.allpts.push(H.startX,H.startY);for(var qe=0;qe+1c[0]&&i.clientXc[1]&&i.clientY=e.desktopTapThreshold2){var I=!e.dragData.didDrag;I&&e.redrawHint("eles",!0),e.dragData.didDrag=!0;for(var L=[],A=0;A0&&e.redrawHint("eles",!0),e.dragData.possibleDragElements=h=[]),t(d,["mouseup","tapend","vmouseup"],n,{cyPosition:{x:l[0],y:l[1]}}),e.dragData.didDrag||e.hoverData.dragged||t(d,["click","tap","vclick"],n,{cyPosition:{x:l[0],y:l[1]}}),d!=p||e.dragData.didDrag||e.hoverData.selecting||null!=d&&d._private.selectable&&(e.hoverData.dragging||("additive"===s.selectionType()||v?d.selected()?d.unselect():d.select():v||(s.$(":selected").unmerge(d).unselect(),d.select())),e.redrawHint("eles",!0)),e.hoverData.selecting){var y=[],m=e.getAllInBox(u[0],u[1],u[2],u[3]);e.redrawHint("select",!0),m.length>0&&e.redrawHint("eles",!0);for(var b=0;b=0&&k>=g&&m>=0&&k>=m&&y>=0&&T>=y&&b>=0&&T>=b;var p=n.pan(),v=n.zoom();x=N(g,y,m,b),w=M(g,y,m,b),_=[(g+m)/2,(y+b)/2],E=[(_[0]-p.x)/v,(_[1]-p.y)/v];var f=200,C=f*f;if(C>w&&!r.touches[2]){var B=e.findNearestElement(s[0],s[1],!0,!0),z=e.findNearestElement(s[2],s[3],!0,!0);return B&&B.isNode()?(B.activate().trigger(a(r,{type:"cxttapstart",cyPosition:{x:s[0],y:s[1]}})),e.touchData.start=B):z&&z.isNode()?(z.activate().trigger(a(r,{type:"cxttapstart",cyPosition:{x:s[0],y:s[1]}})),e.touchData.start=z):(n.trigger(a(r,{type:"cxttapstart",cyPosition:{x:s[0],y:s[1]}})),e.touchData.start=null),e.touchData.start&&(e.touchData.start._private.grabbed=!1),e.touchData.cxt=!0,e.touchData.cxtDragged=!1,e.data.bgActivePosistion=void 0,void e.redraw()}}if(r.touches[2]);else if(r.touches[1]);else if(r.touches[0]){var O=e.findNearestElement(s[0],s[1],!0,!0);if(null!=O&&(O.activate(),e.touchData.start=O,O.isNode()&&e.nodeIsDraggable(O))){var I=e.dragData.touchDragEles=[];if(e.redrawHint("eles",!0),e.redrawHint("drag",!0),O.selected())for(var L=n.$(function(){return this.isNode()&&this.selected()}),A=0;A=Y||V>=q){e.touchData.cxt=!1,e.touchData.start&&(e.touchData.start.unactivate(),e.touchData.start=null),e.data.bgActivePosistion=void 0,e.redrawHint("select",!0);var $=a(r,{type:"cxttapend",cyPosition:{x:c[0],y:c[1]}});e.touchData.start?e.touchData.start.trigger($):l.trigger($)}}if(s&&e.touchData.cxt){var $=a(r,{type:"cxtdrag",cyPosition:{x:c[0],y:c[1]}});e.data.bgActivePosistion=void 0,e.redrawHint("select",!0),e.touchData.start?e.touchData.start.trigger($):l.trigger($),e.touchData.start&&(e.touchData.start._private.grabbed=!1),e.touchData.cxtDragged=!0;var H=e.findNearestElement(c[0],c[1],!0,!0);e.touchData.cxtOver&&H===e.touchData.cxtOver||(e.touchData.cxtOver&&e.touchData.cxtOver.trigger(a(r,{type:"cxtdragout",cyPosition:{x:c[0],y:c[1]}})),e.touchData.cxtOver=H,H&&H.trigger(a(r,{type:"cxtdragover",cyPosition:{x:c[0],y:c[1]}})))}else if(s&&r.touches[2]&&l.boxSelectionEnabled())r.preventDefault(),e.data.bgActivePosistion=void 0,this.lastThreeTouch=+new Date,e.touchData.selecting=!0,e.redrawHint("select",!0),i&&0!==i.length&&void 0!==i[0]?(i[2]=(c[0]+c[2]+c[4])/3,i[3]=(c[1]+c[3]+c[5])/3):(i[0]=(c[0]+c[2]+c[4])/3,i[1]=(c[1]+c[3]+c[5])/3,i[2]=(c[0]+c[2]+c[4])/3+1,i[3]=(c[1]+c[3]+c[5])/3+1),i[4]=1,e.touchData.selecting=!0,e.redraw();else if(s&&r.touches[1]&&l.zoomingEnabled()&&l.panningEnabled()&&l.userZoomingEnabled()&&l.userPanningEnabled()){r.preventDefault(),e.data.bgActivePosistion=void 0,e.redrawHint("select",!0);var W=e.dragData.touchDragEles;if(W){e.redrawHint("drag",!0);for(var Z=0;Z=e.touchTapThreshold2){for(var W=e.dragData.touchDragEles,pe=!e.dragData.didDrag,ve=0;vee.touchTapThreshold2&&(e.touchData.singleTouchMoved=!0);if(s&&(null==de||de.isEdge())&&l.panningEnabled()&&l.userPanningEnabled()){r.preventDefault(),e.swipePanning?l.panBy({x:v[0]*h,y:v[1]*h}):O>=e.touchTapThreshold2&&(e.swipePanning=!0,l.panBy({x:k*h,y:C*h}),de&&(de.unactivate(),e.data.bgActivePosistion||(e.data.bgActivePosistion={x:c[0],y:c[1]}),e.redrawHint("select",!0),e.touchData.start=null));var p=e.projectIntoViewport(r.touches[0].clientX,r.touches[0].clientY);c[0]=p[0],c[1]=p[1]}}for(var f=0;f0?e.redrawHint("eles",!0):e.redraw()}var x=!1;if(null!=n&&(n._private.active=!1,x=!0,n.unactivate()),r.touches[2])e.data.bgActivePosistion=void 0,e.redrawHint("select",!0);else if(r.touches[1]);else if(r.touches[0]);else if(!r.touches[0]){e.data.bgActivePosistion=void 0,e.redrawHint("select",!0);var w=e.dragData.touchDragEles;if(null!=n){var _=n._private.grabbed;c(w),e.redrawHint("drag",!0),e.redrawHint("eles",!0),_&&n.trigger("free"),t(n,["touchend","tapend","vmouseup"],r,{cyPosition:{x:d[0],y:d[1]}}),n.unactivate(),e.touchData.start=null}else{var E=e.findNearestElement(d[0],d[1],!0,!0);t(E,["touchend","tapend","vmouseup"],r,{cyPosition:{x:d[0],y:d[1]}})}var D=e.touchData.startPosition[0]-d[0],S=D*D,k=e.touchData.startPosition[1]-d[1],T=k*k,P=S+T,C=P*u*u;null!=n&&!e.dragData.didDrag&&n._private.selectable&&C=e*e+t*t}},e("triangle",n.generateUnitNgonPointsFitToSquare(3,0)),e("square",n.generateUnitNgonPointsFitToSquare(4,0)),t.rectangle=t.square,t.roundrectangle={name:"roundrectangle",points:n.generateUnitNgonPointsFitToSquare(4,0),draw:function(e,t,n,i,a){r.nodeShapeImpl(this.name)(e,t,n,i,a)},intersectLine:function(e,t,r,i,a,o,s){return n.roundRectangleIntersectLine(a,o,e,t,r,i,s)},checkPoint:function(e,t,r,i,a,o,s){var l=n.getRoundRectangleRadius(i,a);if(n.pointInsidePolygon(e,t,this.points,o,s,i,a-2*l,[0,-1],r))return!0;if(n.pointInsidePolygon(e,t,this.points,o,s,i-2*l,a,[0,-1],r))return!0;var u=function(e,t,r,n,i,a,o){return e-=r,t-=n,e/=i/2+o,t/=a/2+o,1>=e*e+t*t};return u(e,t,o-i/2+l,s-a/2+l,2*l,2*l,r)?!0:u(e,t,o+i/2-l,s-a/2+l,2*l,2*l,r)?!0:u(e,t,o+i/2-l,s+a/2-l,2*l,2*l,r)?!0:u(e,t,o-i/2+l,s+a/2-l,2*l,2*l,r)?!0:!1}},e("diamond",[0,1,1,0,0,-1,-1,0]),e("pentagon",n.generateUnitNgonPointsFitToSquare(5,0)),e("hexagon",n.generateUnitNgonPointsFitToSquare(6,0)),e("heptagon",n.generateUnitNgonPointsFitToSquare(7,0)),e("octagon",n.generateUnitNgonPointsFitToSquare(8,0));var i=new Array(20),a=n.generateUnitNgonPoints(5,0),o=n.generateUnitNgonPoints(5,Math.PI/5),s=.5*(3-Math.sqrt(5));s*=1.57;for(var l=0;ll;l++)i[4*l]=a[2*l],i[4*l+1]=a[2*l+1],i[4*l+2]=o[2*l],i[4*l+3]=o[2*l+1];i=n.fitPolygonToSquare(i),e("star",i),e("vee",[-1,-1,0,-.333,1,-1,0,1]),e("rhomboid",[-1,-1,.333,-1,1,1,-.333,1]),t.makePolygon=function(r){var n,i=r.join("$"),a="polygon-"+i;return(n=t[a])?n:e(a,r)}},t.exports=i},{"../../../math":79}],61:[function(e,t,r){"use strict";var n=e("../../../util"),i={};i.timeToRender=function(){return this.redrawTotalTime/this.redrawCount};var a=1e3/60,o=1e3;i.redraw=function(e){e=e||n.staticEmptyObject();var t=this,r=e.forcedContext;void 0===t.averageRedrawTime&&(t.averageRedrawTime=0),void 0===t.lastRedrawTime&&(t.lastRedrawTime=0);var i=t.lastRedrawTime;i=a>i?a:i,i=o>i?i:o,void 0===t.lastDrawTime&&(t.lastDrawTime=0);var s=Date.now(),l=s-t.lastDrawTime,u=l>=i;return r||u&&!t.currentlyDrawing?(t.requestedFrame=!0,t.currentlyDrawing=!0,void(t.renderOptions=e)):void(t.skipFrame=!0)},i.startRenderLoop=function(){var e=this,t=function(){if(!e.destroyed){if(e.requestedFrame&&!e.skipFrame){var r=n.performanceNow();e.render(e.renderOptions);var i=e.lastRedrawTime=n.performanceNow();void 0===e.averageRedrawTime&&(e.averageRedrawTime=i-r),void 0===e.redrawCount&&(e.redrawCount=0),e.redrawCount++,void 0===e.redrawTotalTime&&(e.redrawTotalTime=0);var a=i-r;e.redrawTotalTime+=a,e.lastRedrawTime=a,e.averageRedrawTime=e.averageRedrawTime/2+a/2,e.requestedFrame=!1}e.skipFrame=!1,n.requestAnimationFrame(t)}};n.requestAnimationFrame(t)},t.exports=i},{"../../../util":94}],62:[function(e,t,r){"use strict";var n,i={};i.arrowShapeImpl=function(e){return(n||(n={polygon:function(e,t){for(var r=0;rn)){e.textAlign="center",e.textBaseline="middle";var o=t._private.rscratch;if(i.number(o.labelX)&&i.number(o.labelY)){var s,l=t._private.style,u="autorotate"===l["edge-text-rotation"].strValue;u?(s=o.labelAngle,e.translate(o.labelX,o.labelY),e.rotate(s),this.drawText(e,t,0,0),e.rotate(-s),e.translate(-o.labelX,-o.labelY)):this.drawText(e,t,o.labelX,o.labelY)}}}},a.drawNodeText=function(e,t){var r=t._private.style.label.strValue;if(r&&!r.match(/^\s+$/)){var n=t._private.style["font-size"].pfValue*t.cy().zoom(),a=t._private.style["min-zoomed-font-size"].pfValue;if(!(a>n)){var o=t._private.style["text-halign"].strValue,s=t._private.style["text-valign"].strValue,l=t._private.rscratch;if(i.number(l.labelX)&&i.number(l.labelY)){switch(o){case"left":e.textAlign="right";break;case"right":e.textAlign="left";break;default:e.textAlign="center"}switch(s){case"top":e.textBaseline="bottom";break;case"bottom":e.textBaseline="top";break;default:e.textBaseline="middle"}this.drawText(e,t,l.labelX,l.labelY)}}}},a.getFontCache=function(e){var t;this.fontCaches=this.fontCaches||[];for(var r=0;r0||b>0&&m>0){var x=4+b/2;t.isNode()&&("top"===h?i-=x:"bottom"===h&&(i+=x),"left"===d?r-=x:"right"===d&&(r+=x));var w=s.labelWidth,_=s.labelHeight,E=r;d&&("center"==d?E-=w/2:"left"==d&&(E-=w));var D=i;if(t.isNode()?"top"==h?D-=_:"center"==h&&(D-=_/2):D-=_/2,"autorotate"===o["edge-text-rotation"].strValue?(i=0,w+=4,E=r-w/2,D=i-_/2):(E-=x,D-=x,_+=2*x,w+=2*x),y>0){var S=e.fillStyle,k=o["text-background-color"].value;e.fillStyle="rgba("+k[0]+","+k[1]+","+k[2]+","+y*u+")";var T=o["text-background-shape"].strValue;"roundrectangle"==T?n(e,E,D,w,_,2):e.fillRect(E,D,w,_),e.fillStyle=S}if(b>0&&m>0){var P=e.strokeStyle,C=e.lineWidth,N=o["text-border-color"].value,M=o["text-border-style"].value;if(e.strokeStyle="rgba("+N[0]+","+N[1]+","+N[2]+","+m*u+")",e.lineWidth=b,e.setLineDash)switch(M){case"dotted":e.setLineDash([1,1]);break;case"dashed":e.setLineDash([4,2]);break;case"double":e.lineWidth=b/4,e.setLineDash([]);break;case"solid":e.setLineDash([])}if(e.strokeRect(E,D,w,_),"double"===M){var B=b/2;e.strokeRect(E+B,D+B,w-2*B,_-2*B)}e.setLineDash&&e.setLineDash([]),e.lineWidth=C,e.strokeStyle=P}}var z=2*o["text-outline-width"].pfValue;if(z>0&&(e.lineWidth=z),"wrap"===o["text-wrap"].value){var O=l.labelWrapCachedLines,I=s.labelHeight/O.length;switch(h){case"top":i-=(O.length-1)*I;break;case"bottom":break;default:case"center":i-=(O.length-1)*I/2}for(var L=0;L0&&e.strokeText(O[L],r,i),e.fillText(O[L],r,i),i+=I}else z>0&&e.strokeText(c,r,i),e.fillText(c,r,i);this.shadowStyle(e,"transparent",0)}}},t.exports=a},{"../../../is":77}],66:[function(e,t,r){"use strict";var n=e("../../../is"),i={};i.drawNode=function(e,t,r){var i,a,o=this,s=t._private.style,l=t._private.rscratch,u=t._private,c=u.position;if(n.number(c.x)&&n.number(c.y)){var d,h=this.usePaths(),p=e,v=!1,f=s["overlay-padding"].pfValue,g=s["overlay-opacity"].value,y=s["overlay-color"].value;if(!r||0!==g){var m=t.effectiveOpacity();if(0!==m)if(i=t.width()+s["padding-left"].pfValue+s["padding-right"].pfValue,a=t.height()+s["padding-top"].pfValue+s["padding-bottom"].pfValue,e.lineWidth=s["border-width"].pfValue,void 0!==r&&r)g>0&&(this.fillStyle(e,y[0],y[1],y[2],g),o.nodeShapes.roundrectangle.draw(e,t._private.position.x,t._private.position.y,i+2*f,a+2*f),e.fill());else{var b,x=s["background-image"].value[2]||s["background-image"].value[1];if(void 0!==x){b=this.getCachedImage(x,function(){o.data.canvasNeedsRedraw[o.NODE]=!0,o.data.canvasNeedsRedraw[o.DRAG]=!0,o.drawingImage=!0,o.redraw()});var w=u.backgrounding;u.backgrounding=!b.complete,w!==u.backgrounding&&t.updateStyle(!1)}var _=s["background-color"].value,E=s["border-color"].value,D=s["border-style"].value;this.fillStyle(e,_[0],_[1],_[2],s["background-opacity"].value*m),this.strokeStyle(e,E[0],E[1],E[2],s["border-opacity"].value*m);var S=s["shadow-blur"].pfValue,k=s["shadow-opacity"].value,T=s["shadow-color"].value,P=s["shadow-offset-x"].pfValue,C=s["shadow-offset-y"].pfValue;if(this.shadowStyle(e,T,k,S,P,C),e.lineJoin="miter",e.setLineDash)switch(D){case"dotted":e.setLineDash([1,1]);break;case"dashed":e.setLineDash([4,2]);break;case"solid":case"double":e.setLineDash([])}var N=s.shape.strValue;if(h){var M=N+"$"+i+"$"+a;e.translate(c.x,c.y),l.pathCacheKey===M?(d=e=l.pathCache,v=!0):(d=e=new Path2D,l.pathCacheKey=M,l.pathCache=d)}if(!v){var B=c;h&&(B={x:0,y:0}),o.nodeShapes[this.getNodeShape(t)].draw(e,B.x,B.y,i,a)}e=p,h?e.fill(d):e.fill(),this.shadowStyle(e,"transparent",0),void 0!==x&&b.complete&&this.drawInscribedImage(e,b,t);var z=s["background-blacken"].value,O=s["border-width"].pfValue;if(this.hasPie(t)&&(this.drawPie(e,t,m),(0!==z||0!==O)&&(h||o.nodeShapes[this.getNodeShape(t)].draw(e,c.x,c.y,i,a))),z>0?(this.fillStyle(e,0,0,0,z),h?e.fill(d):e.fill()):0>z&&(this.fillStyle(e,255,255,255,-z),h?e.fill(d):e.fill()),O>0&&(h?e.stroke(d):e.stroke(),"double"===D)){e.lineWidth=s["border-width"].pfValue/3;var I=e.globalCompositeOperation;e.globalCompositeOperation="destination-out",h?e.stroke(d):e.stroke(),e.globalCompositeOperation=I}h&&e.translate(-c.x,-c.y),e.setLineDash&&e.setLineDash([])}}}},i.hasPie=function(e){return e=e[0],e._private.hasPie},i.drawPie=function(e,t,r){t=t[0];var n=t._private,i=t.cy().style(),a=n.style,o=a["pie-size"],s=t.width(),l=t.height(),u=n.position.x,c=n.position.y,d=Math.min(s,l)/2,h=0,p=this.usePaths();p&&(u=0,c=0),"%"===o.units?d=d*o.value/100:void 0!==o.pfValue&&(d=o.pfValue/2);for(var v=1;v<=i.pieBackgroundN;v++){var f=a["pie-"+v+"-background-size"].value,g=a["pie-"+v+"-background-color"].value,y=a["pie-"+v+"-background-opacity"].value*r,m=f/100;m+h>1&&(m=1-h);var b=1.5*Math.PI+2*Math.PI*h,x=2*Math.PI*m,w=b+x;0===f||h>=1||h+m>1||(e.beginPath(),e.moveTo(u,c),e.arc(u,c,d,b,w),e.closePath(),this.fillStyle(e,g[0],g[1],g[2],y),e.fill(),h+=m)}},t.exports=i},{"../../../is":77}],67:[function(e,t,r){"use strict";var n={},i=e("../../../util"),a=e("../../../math"),o=100;n.getPixelRatio=function(){var e=this.data.contexts[0];if(null!=this.forcedPixelRatio)return this.forcedPixelRatio;var t=e.backingStorePixelRatio||e.webkitBackingStorePixelRatio||e.mozBackingStorePixelRatio||e.msBackingStorePixelRatio||e.oBackingStorePixelRatio||e.backingStorePixelRatio||1;return(window.devicePixelRatio||1)/t},n.paintCache=function(e){for(var t,r=this.paintCaches=this.paintCaches||[],n=!0,i=0;i0?(e.shadowBlur=n*o,e.shadowColor="rgba("+t[0]+","+t[1]+","+t[2]+","+r+")",e.shadowOffsetX=i*o,e.shadowOffsetY=a*o):(e.shadowBlur=0,e.shadowColor="transparent"))},n.matchCanvasSize=function(e){var t=this,r=t.data,n=e.clientWidth,i=e.clientHeight,a=t.getPixelRatio(),o=t.motionBlurPxRatio;(e===t.data.bufferCanvases[t.MOTIONBLUR_BUFFER_NODE]||e===t.data.bufferCanvases[t.MOTIONBLUR_BUFFER_DRAG])&&(a=o);var s,l=n*a,u=i*a;if(l!==t.canvasWidth||u!==t.canvasHeight){t.fontCaches=null;var c=r.canvasContainer;c.style.width=n+"px",c.style.height=i+"px";for(var d=0;d=a&&(s=r.bufferCanvases[t.TEXTURE_BUFFER],t.textureMult=2,s.width=l*t.textureMult,s.height=u*t.textureMult),t.canvasWidth=l,t.canvasHeight=u}},n.renderTo=function(e,t,r,n){this.render({forcedContext:e,forcedZoom:t,forcedPan:r,drawAllLayers:!0,forcedPxRatio:n})},n.render=function(e){function t(e,t,r,n,i){var a=e.globalCompositeOperation;e.globalCompositeOperation="destination-out",h.fillStyle(e,255,255,255,h.motionBlurTransparency),e.fillRect(t,r,n,i),e.globalCompositeOperation=a}function r(e,r){var n,i,a,o;h.clearingMotionBlur||e!==f.bufferContexts[h.MOTIONBLUR_BUFFER_NODE]&&e!==f.bufferContexts[h.MOTIONBLUR_BUFFER_DRAG]?(n=C,i=T,a=h.canvasWidth,o=h.canvasHeight):(n={x:P.x*b,y:P.y*b},i=k*b,a=h.canvasWidth*b,o=h.canvasHeight*b),e.setTransform(1,0,0,1,0,0),"motionBlur"===r?t(e,0,0,a,o):s||void 0!==r&&!r||e.clearRect(0,0,a,o),l||(e.translate(n.x,n.y),e.scale(i,i)),d&&e.translate(d.x,d.y),c&&e.scale(c,c)}function n(e,t){for(var r=e.eles,n=0;nh.minMbLowQualFrames&&(h.motionBlurPxRatio=h.mbPxRBlurry)),h.clearingMotionBlur&&(h.motionBlurPxRatio=1),h.textureDrawLastFrame&&!y&&(g[h.NODE]=!0,g[h.SELECT_BOX]=!0);var D=h.getCachedEdges(),S=v.style()._private.coreStyle,k=v.zoom(),T=void 0!==c?c:k,P=v.pan(),C={x:P.x,y:P.y},N={zoom:k,pan:{x:P.x,y:P.y}},M=h.prevViewport,B=void 0===M||N.zoom!==M.zoom||N.pan.x!==M.pan.x||N.pan.y!==M.pan.y;B||w&&!x||(h.motionBlurPxRatio=1),d&&(C=d),T*=p,C.x*=p,C.y*=p;var z={drag:{nodes:[],edges:[],eles:[]},nondrag:{nodes:[],edges:[],eles:[]}};if(y||(h.textureDrawLastFrame=!1),y){h.textureDrawLastFrame=!0;var O;if(!h.textureCache){h.textureCache={},O=h.textureCache.bb=v.elements().boundingBox(),h.textureCache.texture=h.data.bufferCanvases[h.TEXTURE_BUFFER];var I=h.data.bufferContexts[h.TEXTURE_BUFFER];I.setTransform(1,0,0,1,0,0),I.clearRect(0,0,h.canvasWidth*h.textureMult,h.canvasHeight*h.textureMult),h.render({forcedContext:I,drawOnlyNodeLayer:!0,forcedPxRatio:p*h.textureMult});var N=h.textureCache.viewport={zoom:v.zoom(),pan:v.pan(),width:h.canvasWidth,height:h.canvasHeight};N.mpan={x:(0-N.pan.x)/N.zoom,y:(0-N.pan.y)/N.zoom}}g[h.DRAG]=!1,g[h.NODE]=!1;var L=f.contexts[h.NODE],A=h.textureCache.texture,N=h.textureCache.viewport;O=h.textureCache.bb,L.setTransform(1,0,0,1,0,0),m?t(L,0,0,N.width,N.height):L.clearRect(0,0,N.width,N.height);var R=S["outside-texture-bg-color"].value,V=S["outside-texture-bg-opacity"].value;h.fillStyle(L,R[0],R[1],R[2],V),L.fillRect(0,0,N.width,N.height);var k=v.zoom();r(L,!1),L.clearRect(N.mpan.x,N.mpan.y,N.width/N.zoom/p,N.height/N.zoom/p),L.drawImage(A,N.mpan.x,N.mpan.y,N.width/N.zoom/p,N.height/N.zoom/p)}else h.textureOnViewport&&!s&&(h.textureCache=null);var F=h.pinching||h.hoverData.dragging||h.swipePanning||h.data.wheelZooming||h.hoverData.draggingEles,j=h.hideEdgesOnViewport&&F,q=h.hideLabelsOnViewport&&F;if(g[h.DRAG]||g[h.NODE]||l||u){j||h.findEdgeControlPoints(D);for(var X=h.getCachedZSortedEles(),Y=v.extent(),$=0;$0&&(L.strokeStyle="rgba("+S["selection-box-border-color"].value[0]+","+S["selection-box-border-color"].value[1]+","+S["selection-box-border-color"].value[2]+","+S["selection-box-opacity"].value+")",L.strokeRect(h.selection[0],h.selection[1],h.selection[2]-h.selection[0],h.selection[3]-h.selection[1]))}if(f.bgActivePosistion&&!h.hoverData.selecting){var k=h.cy.zoom(),Q=f.bgActivePosistion;L.fillStyle="rgba("+S["active-bg-color"].value[0]+","+S["active-bg-color"].value[1]+","+S["active-bg-color"].value[2]+","+S["active-bg-opacity"].value+")",L.beginPath(),L.arc(Q.x,Q.y,S["active-bg-size"].pfValue/k,0,2*Math.PI),L.fill()}var ee=h.lastRedrawTime;if(h.showFps&&ee){ee=Math.round(ee);var te=Math.round(1e3/ee);L.setTransform(1,0,0,1,0,0),L.fillStyle="rgba(255, 0, 0, 0.75)",L.strokeStyle="rgba(255, 0, 0, 0.75)",L.lineWidth=1,L.fillText("1 frame = "+ee+" ms = "+te+" fps",0,20);var re=60;L.strokeRect(0,30,250,20),L.fillRect(0,30,250*Math.min(te/re,1),20)}l||(g[h.SELECT_BOX]=!1)}if(m&&1!==b){var ne=f.contexts[h.NODE],ie=h.data.bufferCanvases[h.MOTIONBLUR_BUFFER_NODE],ae=f.contexts[h.DRAG],oe=h.data.bufferCanvases[h.MOTIONBLUR_BUFFER_DRAG],se=function(e,r,n){e.setTransform(1,0,0,1,0,0),n||!E?e.clearRect(0,0,h.canvasWidth,h.canvasHeight):t(e,0,0,h.canvasWidth,h.canvasHeight);var i=b;e.drawImage(r,0,0,h.canvasWidth*i,h.canvasHeight*i,0,0,h.canvasWidth,h.canvasHeight)};(g[h.NODE]||U[h.NODE])&&(se(ne,ie,U[h.NODE]),g[h.NODE]=!1),(g[h.DRAG]||U[h.DRAG])&&(se(ae,oe,U[h.DRAG]),g[h.DRAG]=!1)}h.currentlyDrawing=!1,h.prevViewport=N,h.clearingMotionBlur&&(h.clearingMotionBlur=!1,h.motionBlurCleared=!0,h.motionBlur=!0),m&&(h.motionBlurTimeout=setTimeout(function(){h.motionBlurTimeout=null,h.clearedForMotionBlur[h.NODE]=!1,h.clearedForMotionBlur[h.DRAG]=!1,h.motionBlur=!1,h.clearingMotionBlur=!y,h.mbFrames=0,g[h.NODE]=!0,g[h.DRAG]=!0,h.redraw()},o)),h.drawingImage=!1,s||h.initrender||(h.initrender=!0,v.trigger("initrender")),s||v.triggerOnRender()},t.exports=n},{"../../../math":79,"../../../util":94}],68:[function(e,t,r){"use strict";var n=e("../../../math"),i={};i.drawPolygonPath=function(e,t,r,n,i,a){var o=n/2,s=i/2;e.beginPath&&e.beginPath(),e.moveTo(t+o*a[0],r+s*a[1]);for(var l=1;l0&&a>0)if(c.clearRect(0,0,i,a),e.bg&&(c.fillStyle=e.bg,c.rect(0,0,i,a),c.fill()),c.globalCompositeOperation="source-over",e.full)this.render({forcedContext:c,drawAllLayers:!0,forcedZoom:o,forcedPan:{x:-r.x1*o,y:-r.y1*o},forcedPxRatio:1});else{var d=t.pan(),h={x:d.x*o,y:d.y*o},p=t.zoom()*o;this.render({forcedContext:c,drawAllLayers:!0,forcedZoom:p,forcedPan:h,forcedPxRatio:1})}return u},i.png=function(e){return this.bufferCanvasImage(e).toDataURL("image/png")},i.jpg=function(e){return this.bufferCanvasImage(e).toDataURL("image/jpeg")},t.exports=i},{"../../../is":77}],70:[function(e,t,r){"use strict";function n(e){var t=this;t.data={canvases:new Array(s.CANVAS_LAYERS),contexts:new Array(s.CANVAS_LAYERS),canvasNeedsRedraw:new Array(s.CANVAS_LAYERS),bufferCanvases:new Array(s.BUFFER_COUNT),bufferContexts:new Array(s.CANVAS_LAYERS)},t.data.canvasContainer=document.createElement("div");var r=t.data.canvasContainer.style;t.data.canvasContainer.setAttribute("style","-webkit-tap-highlight-color: rgba(0,0,0,0);"),r.position="relative",r.zIndex="0",r.overflow="hidden";var n=e.cy.container();n.appendChild(t.data.canvasContainer),n.setAttribute("style",(n.getAttribute("style")||"")+"-webkit-tap-highlight-color: rgba(0,0,0,0);"); -for(var i=0;io;o++)this[o]=new a;this.length=t},u=l.prototype;i.extend(u,{instanceString:function(){return"fabric"},require:function(e,t){for(var r=0;re?-1:e>t?1:0},t.require(e,"_$_$_cmp"),t.spread(function(e){var t=e.sort(_$_$_cmp);resolve(t)}).then(function(t){for(var i=function(n,i,a){i=Math.min(i,r),a=Math.min(a,r);for(var o=n,s=i,l=[],u=o;a>u;u++){var c=t[n],d=t[i];s>n&&(i>=a||e(c,d)<=0)?(l.push(c),n++):(l.push(d),i++)}for(var u=0;ua;a*=2)for(var o=0;r>o;o+=2*a)i(o,o+a,o+2*a);return t})}});var c=function(e){return e=e||{},function(t,r){var n=this._private.pass.shift();return this.random().pass(n)[e.threadFn](t,r)}};i.extend(u,{randomMap:c({threadFn:"map"}),reduce:c({threadFn:"reduce"}),reduceRight:c({threadFn:"reduceRight"})});var d=u;d.promise=d.run,d.terminate=d.halt=d.stop,d.include=d.require,i.extend(u,{on:s.on(),one:s.on({unbindSelfOnTrigger:!0}),off:s.off(),trigger:s.trigger()}),s.eventAliasesOn(u),t.exports=l},{"./define":41,"./is":77,"./promise":80,"./thread":92,"./util":94,os:void 0}],75:[function(e,t,r){"use strict";(function(){var e,n,i,a,o,s,l,u,c,d,h,p,v,f,g;i=Math.floor,d=Math.min,n=function(e,t){return t>e?-1:e>t?1:0},c=function(e,t,r,a,o){var s;if(null==r&&(r=0),null==o&&(o=n),0>r)throw new Error("lo must be non-negative");for(null==a&&(a=e.length);a>r;)s=i((r+a)/2),o(t,e[s])<0?a=s:r=s+1;return[].splice.apply(e,[r,r-r].concat(t)),t},s=function(e,t,r){return null==r&&(r=n),e.push(t),f(e,0,e.length-1,r)},o=function(e,t){var r,i;return null==t&&(t=n),r=e.pop(),e.length?(i=e[0],e[0]=r,g(e,0,t)):i=r,i},u=function(e,t,r){var i;return null==r&&(r=n),i=e[0],e[0]=t,g(e,0,r),i},l=function(e,t,r){var i;return null==r&&(r=n),e.length&&r(e[0],t)<0&&(i=[e[0],t],t=i[0],e[0]=i[1],g(e,0,r)),t},a=function(e,t){var r,a,o,s,l,u;for(null==t&&(t=n),s=function(){u=[];for(var t=0,r=i(e.length/2);r>=0?r>t:t>r;r>=0?t++:t--)u.push(t);return u}.apply(this).reverse(),l=[],a=0,o=s.length;o>a;a++)r=s[a],l.push(g(e,r,t));return l},v=function(e,t,r){var i;return null==r&&(r=n),i=e.indexOf(t),-1!==i?(f(e,0,i,r),g(e,i,r)):void 0},h=function(e,t,r){var i,o,s,u,c;if(null==r&&(r=n),o=e.slice(0,t),!o.length)return o;for(a(o,r),c=e.slice(t),s=0,u=c.length;u>s;s++)i=c[s],l(o,i,r);return o.sort(r).reverse()},p=function(e,t,r){var i,s,l,u,h,p,v,f,g,y;if(null==r&&(r=n),10*t<=e.length){if(u=e.slice(0,t).sort(r),!u.length)return u;for(l=u[u.length-1],f=e.slice(t),h=0,v=f.length;v>h;h++)i=f[h],r(i,l)<0&&(c(u,i,0,null,r),u.pop(),l=u[u.length-1]);return u}for(a(e,r),y=[],s=p=0,g=d(t,e.length);g>=0?g>p:p>g;s=g>=0?++p:--p)y.push(o(e,r));return y},f=function(e,t,r,i){var a,o,s;for(null==i&&(i=n),a=e[r];r>t&&(s=r-1>>1,o=e[s],i(a,o)<0);)e[r]=o,r=s;return e[r]=a},g=function(e,t,r){var i,a,o,s,l;for(null==r&&(r=n),a=e.length,l=t,o=e[t],i=2*t+1;a>i;)s=i+1,a>s&&!(r(e[i],e[s])<0)&&(i=s),e[t]=e[i],t=i,i=2*t+1;return e[t]=o,f(e,l,t,r)},e=function(){function e(e){this.cmp=null!=e?e:n,this.nodes=[]}return e.push=s,e.pop=o,e.replace=u,e.pushpop=l,e.heapify=a,e.updateItem=v,e.nlargest=h,e.nsmallest=p,e.prototype.push=function(e){return s(this.nodes,e,this.cmp)},e.prototype.pop=function(){return o(this.nodes,this.cmp)},e.prototype.peek=function(){return this.nodes[0]},e.prototype.contains=function(e){return-1!==this.nodes.indexOf(e)},e.prototype.replace=function(e){return u(this.nodes,e,this.cmp)},e.prototype.pushpop=function(e){return l(this.nodes,e,this.cmp)},e.prototype.heapify=function(){return a(this.nodes,this.cmp)},e.prototype.updateItem=function(e){return v(this.nodes,e,this.cmp)},e.prototype.clear=function(){return this.nodes=[]},e.prototype.empty=function(){return 0===this.nodes.length},e.prototype.size=function(){return this.nodes.length},e.prototype.clone=function(){var t;return t=new e,t.nodes=this.nodes.slice(0),t},e.prototype.toArray=function(){return this.nodes.slice(0)},e.prototype.insert=e.prototype.push,e.prototype.top=e.prototype.peek,e.prototype.front=e.prototype.peek,e.prototype.has=e.prototype.contains,e.prototype.copy=e.prototype.clone,e}(),function(e,n){return"function"==typeof define&&define.amd?define([],n):"object"==typeof r?t.exports=n():e.Heap=n()}(this,function(){return e})}).call(this)},{}],76:[function(e,t,r){"use strict";var n=e("./window"),i=e("./is"),a=e("./core"),o=e("./extension"),s=e("./jquery-plugin"),l=e("./stylesheet"),u=e("./thread"),c=e("./fabric"),d=function(e){return void 0===e&&(e={}),i.plainObject(e)?new a(e):i.string(e)?o.apply(o,arguments):void 0};d.version="2.5.1",n&&n.jQuery&&s(n.jQuery,d),d.registerJquery=function(e){s(e,d)},d.stylesheet=d.Stylesheet=l,d.thread=d.Thread=u,d.fabric=d.Fabric=c,t.exports=d},{"./core":34,"./extension":43,"./fabric":74,"./is":77,"./jquery-plugin":78,"./stylesheet":91,"./thread":92,"./window":100}],77:[function(e,t,r){"use strict";var n=e("./window"),i=n?n.navigator:null,a="string",o=typeof{},s="function",l=typeof HTMLElement,u=function(e){return e&&e.instanceString&&c.fn(e.instanceString)?e.instanceString():null},c={defined:function(e){return null!=e},string:function(e){return null!=e&&typeof e==a},fn:function(e){return null!=e&&typeof e===s},array:function(e){return Array.isArray?Array.isArray(e):null!=e&&e instanceof Array},plainObject:function(e){return null!=e&&typeof e===o&&!c.array(e)&&e.constructor===Object},object:function(e){return null!=e&&typeof e===o},number:function(e){return null!=e&&"number"==typeof e&&!isNaN(e)},integer:function(e){return c.number(e)&&Math.floor(e)===e},bool:function(e){return null!=e&&typeof e==typeof!0},htmlElement:function(e){return"undefined"===l?void 0:null!=e&&e instanceof HTMLElement},elementOrCollection:function(e){return c.element(e)||c.collection(e)},element:function(e){return"collection"===u(e)&&e._private.single},collection:function(e){return"collection"===u(e)&&!e._private.single},core:function(e){return"core"===u(e)},style:function(e){return"style"===u(e)},stylesheet:function(e){return"stylesheet"===u(e)},event:function(e){return"event"===u(e)},thread:function(e){return"thread"===u(e)},fabric:function(e){return"fabric"===u(e)},emptyString:function(e){return e?c.string(e)&&(""===e||e.match(/^\s+$/))?!0:!1:!0},nonemptyString:function(e){return e&&c.string(e)&&""!==e&&!e.match(/^\s+$/)?!0:!1},domElement:function(e){return"undefined"==typeof HTMLElement?!1:e instanceof HTMLElement},boundingBox:function(e){return c.plainObject(e)&&c.number(e.x1)&&c.number(e.x2)&&c.number(e.y1)&&c.number(e.y2)},promise:function(e){return c.object(e)&&c.fn(e.then)},touch:function(){return n&&("ontouchstart"in n||n.DocumentTouch&&document instanceof DocumentTouch)},gecko:function(){return"undefined"!=typeof InstallTrigger||"MozAppearance"in document.documentElement.style},webkit:function(){return"undefined"!=typeof webkitURL||"WebkitAppearance"in document.documentElement.style},chromium:function(){return"undefined"!=typeof chrome},khtml:function(){return i&&i.vendor.match(/kde/i)},khtmlEtc:function(){return c.khtml()||c.webkit()||c.chromium()},ms:function(){return i&&i.userAgent.match(/msie|trident|edge/i)},windows:function(){return i&&i.appVersion.match(/Win/i)},mac:function(){return i&&i.appVersion.match(/Mac/i)},linux:function(){return i&&i.appVersion.match(/Linux/i)},unix:function(){return i&&i.appVersion.match(/X11/i)}};t.exports=c},{"./window":100}],78:[function(e,t,r){"use strict";var n=e("./is"),i=function(e){var t=e[0]._cyreg=e[0]._cyreg||{};return t},a=function(e,t){e&&(e.fn.cytoscape||(e.fn.cytoscape=function(r){var a=e(this);if("get"===r)return i(a).cy;if(n.fn(r)){var o=r,s=i(a).cy;if(s&&s.isReady())s.trigger("ready",[],o);else{var l=i(a),u=l.readies=l.readies||[];u.push(o)}}else if(n.plainObject(r))return a.each(function(){var n=e.extend({},r,{container:e(this)[0]});t(n)})},e.cytoscape=t,null==e.fn.cy&&null==e.cy&&(e.fn.cy=e.fn.cytoscape,e.cy=e.cytoscape)))};t.exports=a},{"./is":77}],79:[function(e,t,r){"use strict";var n={};n.signum=function(e){return e>0?1:0>e?-1:0},n.distance=function(e,t){return Math.sqrt(n.sqDistance(e,t))},n.sqDistance=function(e,t){var r=t.x-e.x,n=t.y-e.y;return r*r+n*n},n.qbezierAt=function(e,t,r,n){return(1-n)*(1-n)*e+2*(1-n)*n*t+n*n*r},n.qbezierPtAt=function(e,t,r,i){return{x:n.qbezierAt(e.x,t.x,r.x,i),y:n.qbezierAt(e.y,t.y,r.y,i)}},n.makeBoundingBox=function(e){if(null!=e.x1&&null!=e.y1){if(null!=e.x2&&null!=e.y2&&e.x2>=e.x1&&e.y2>=e.y1)return{x1:e.x1,y1:e.y1,x2:e.x2,y2:e.y2,w:e.x2-e.x1,h:e.y2-e.y1};if(null!=e.w&&null!=e.h&&e.w>=0&&e.h>=0)return{x1:e.x1,y1:e.y1,x2:e.x1+e.w,y2:e.y1+e.h,w:e.w,h:e.h}}},n.boundingBoxesIntersect=function(e,t){return e.x1>t.x2?!1:t.x1>e.x2?!1:e.x2t.y2?!1:t.y1>e.y2?!1:!0},n.inBoundingBox=function(e,t,r){return e.x1<=t&&t<=e.x2&&e.y1<=r&&r<=e.y2},n.pointInBoundingBox=function(e,t){return this.inBoundingBox(e,t.x,t.y)},n.roundRectangleIntersectLine=function(e,t,r,n,i,a,o){var s,l=this.getRoundRectangleRadius(i,a),u=i/2,c=a/2,d=r-u+l-o,h=n-c-o,p=r+u-l+o,v=h;if(s=this.finiteLinesIntersect(e,t,r,n,d,h,p,v,!1),s.length>0)return s;var f=r+u+o,g=n-c+l-o,y=f,m=n+c-l+o;if(s=this.finiteLinesIntersect(e,t,r,n,f,g,y,m,!1),s.length>0)return s;var b=r-u+l-o,x=n+c+o,w=r+u-l+o,_=x;if(s=this.finiteLinesIntersect(e,t,r,n,b,x,w,_,!1),s.length>0)return s;var E=r-u-o,D=n-c+l-o,S=E,k=n+c-l+o;if(s=this.finiteLinesIntersect(e,t,r,n,E,D,S,k,!1),s.length>0)return s;var T,P=r-u+l,C=n-c+l;if(T=this.intersectLineCircle(e,t,r,n,P,C,l+o),T.length>0&&T[0]<=P&&T[1]<=C)return[T[0],T[1]];var N=r+u-l,M=n-c+l;if(T=this.intersectLineCircle(e,t,r,n,N,M,l+o),T.length>0&&T[0]>=N&&T[1]<=M)return[T[0],T[1]];var B=r+u-l,z=n+c-l;if(T=this.intersectLineCircle(e,t,r,n,B,z,l+o),T.length>0&&T[0]>=B&&T[1]>=z)return[T[0],T[1]];var O=r-u+l,I=n+c-l;return T=this.intersectLineCircle(e,t,r,n,O,I,l+o),T.length>0&&T[0]<=O&&T[1]>=I?[T[0],T[1]]:[]},n.inLineVicinity=function(e,t,r,n,i,a,o){var s=o,l=Math.min(r,i),u=Math.max(r,i),c=Math.min(n,a),d=Math.max(n,a);return e>=l-s&&u+s>=e&&t>=c-s&&d+s>=t},n.inBezierVicinity=function(e,t,r,n,i,a,o,s,l){var u={x1:Math.min(r,o,i)-l,x2:Math.max(r,o,i)+l,y1:Math.min(n,s,a)-l,y2:Math.max(n,s,a)+l};return eu.x2||tu.y2?!1:!0},n.solveCubic=function(e,t,r,n,i){t/=e,r/=e,n/=e;var a,o,s,l,u,c,d,h;return o=(3*r-t*t)/9,s=-(27*n)+t*(9*r-2*(t*t)),s/=54,a=o*o*o+s*s,i[1]=0,d=t/3,a>0?(u=s+Math.sqrt(a),u=0>u?-Math.pow(-u,1/3):Math.pow(u,1/3),c=s-Math.sqrt(a),c=0>c?-Math.pow(-c,1/3):Math.pow(c,1/3),i[0]=-d+u+c,d+=(u+c)/2,i[4]=i[2]=-d,d=Math.sqrt(3)*(-c+u)/2,i[3]=d,void(i[5]=-d)):(i[5]=i[3]=0,0===a?(h=0>s?-Math.pow(-s,1/3):Math.pow(s,1/3),i[0]=-d+2*h,void(i[4]=i[2]=-(h+d))):(o=-o,l=o*o*o,l=Math.acos(s/Math.sqrt(l)),h=2*Math.sqrt(o),i[0]=-d+h*Math.cos(l/3),i[2]=-d+h*Math.cos((l+2*Math.PI)/3),void(i[4]=-d+h*Math.cos((l+4*Math.PI)/3))))},n.sqDistanceToQuadraticBezier=function(e,t,r,n,i,a,o,s){var l=1*r*r-4*r*i+2*r*o+4*i*i-4*i*o+o*o+n*n-4*n*a+2*n*s+4*a*a-4*a*s+s*s,u=9*r*i-3*r*r-3*r*o-6*i*i+3*i*o+9*n*a-3*n*n-3*n*s-6*a*a+3*a*s,c=3*r*r-6*r*i+r*o-r*e+2*i*i+2*i*e-o*e+3*n*n-6*n*a+n*s-n*t+2*a*a+2*a*t-s*t,d=1*r*i-r*r+r*e-i*e+n*a-n*n+n*t-a*t,h=[];this.solveCubic(l,u,c,d,h);for(var p=1e-7,v=[],f=0;6>f;f+=2)Math.abs(h[f+1])=0&&h[f]<=1&&v.push(h[f]);v.push(1),v.push(0);for(var g,y,m,b,x=-1,w=0;w=0?x>b&&(x=b,g=v[w]):(x=b,g=v[w]);return x},n.sqDistanceToFiniteLine=function(e,t,r,n,i,a){var o=[e-r,t-n],s=[i-r,a-n],l=s[0]*s[0]+s[1]*s[1],u=o[0]*o[0]+o[1]*o[1],c=o[0]*s[0]+o[1]*s[1],d=c*c/l;return 0>c?u:d>l?(e-i)*(e-i)+(t-a)*(t-a):u-d},n.pointInsidePolygonPoints=function(e,t,r){for(var n,i,a,o,s,l=0,u=0,c=0;c=e&&e>=a||e>=n&&a>=e))continue;s=(e-n)/(a-n)*(o-i)+i,s>t&&l++,t>s&&u++}return l%2===0?!1:!0},n.pointInsidePolygon=function(e,t,r,i,a,o,s,l,u){var c,d=new Array(r.length);null!=l[0]?(c=Math.atan(l[1]/l[0]),l[0]<0?c+=Math.PI/2:c=-c-Math.PI/2):c=l;for(var h=Math.cos(-c),p=Math.sin(-c),v=0;v0){var g=this.expandPolygon(d,-u);f=this.joinLines(g)}else f=d;return n.pointInsidePolygonPoints(e,t,f)},n.joinLines=function(e){for(var t,r,n,i,a,o,s,l,u=new Array(e.length/2),c=0;cu)return[];var c=u/l;return[(r-e)*c+e,(n-t)*c+t]},n.intersectLineCircle=function(e,t,r,n,i,a,o){var s=[r-e,n-t],l=[i,a],u=[e-i,t-a],c=s[0]*s[0]+s[1]*s[1],d=2*(u[0]*s[0]+u[1]*s[1]),l=u[0]*u[0]+u[1]*u[1]-o*o,h=d*d-4*c*l;if(0>h)return[];var p=(-d+Math.sqrt(h))/(2*c),v=(-d-Math.sqrt(h))/(2*c),f=Math.min(p,v),g=Math.max(p,v),y=[];if(f>=0&&1>=f&&y.push(f),g>=0&&1>=g&&y.push(g),0===y.length)return[];var m=y[0]*s[0]+e,b=y[0]*s[1]+t;if(y.length>1){if(y[0]==y[1])return[m,b];var x=y[1]*s[0]+e,w=y[1]*s[1]+t;return[m,b,x,w]}return[m,b]},n.findCircleNearPoint=function(e,t,r,n,i){var a=n-e,o=i-t,s=Math.sqrt(a*a+o*o),l=a/s,u=o/s;return[e+l*r,t+u*r]},n.findMaxSqDistanceToOrigin=function(e){for(var t,r=1e-6,n=0;nr&&(r=t);return r},n.finiteLinesIntersect=function(e,t,r,n,i,a,o,s,l){var u=(o-i)*(t-a)-(s-a)*(e-i),c=(r-e)*(t-a)-(n-t)*(e-i),d=(s-a)*(r-e)-(o-i)*(n-t);if(0!==d){var h=u/d,p=c/d;return h>=0&&1>=h&&p>=0&&1>=p?[e+h*(r-e),t+h*(n-t)]:l?[e+h*(r-e),t+h*(n-t)]:[]}return 0===u||0===c?[e,r,o].sort()[1]===o?[o,s]:[e,r,i].sort()[1]===i?[i,a]:[i,o,r].sort()[1]===r?[r,n]:[]:[]},n.polygonIntersectLine=function(e,t,r,i,a,o,s,l){for(var u,c=[],d=new Array(r.length),h=0;h0){var v=n.expandPolygon(d,-l);p=n.joinLines(v)}else p=d;for(var f,g,y,m,h=0;ha&&(a=1e-5),[t[0]+a*n[0],t[1]+a*n[1]]},n.generateUnitNgonPointsFitToSquare=function(e,t){var r=n.generateUnitNgonPoints(e,t);return r=n.fitPolygonToSquare(r)},n.fitPolygonToSquare=function(e){for(var t,r,n=e.length/2,i=1/0,a=1/0,o=-(1/0),s=-(1/0),l=0;n>l;l++)t=e[2*l],r=e[2*l+1],i=Math.min(i,t),o=Math.max(o,t),a=Math.min(a,r),s=Math.max(s,r);for(var u=2/(o-i),c=2/(s-a),l=0;n>l;l++)t=e[2*l]=e[2*l]*u,r=e[2*l+1]=e[2*l+1]*c,i=Math.min(i,t),o=Math.max(o,t),a=Math.min(a,r),s=Math.max(s,r);if(-1>a)for(var l=0;n>l;l++)r=e[2*l+1]=e[2*l+1]+(-1-a);return e},n.generateUnitNgonPoints=function(e,t){var r=1/e*2*Math.PI,n=e%2===0?Math.PI/2+r/2:Math.PI/2;n+=t;for(var i,a,o,s=new Array(2*e),l=0;e>l;l++)i=l*r+n,a=s[2*l]=Math.cos(i),o=s[2*l+1]=Math.sin(-i);return s},n.getRoundRectangleRadius=function(e,t){return Math.min(e/4,t/4,8)},t.exports=n},{}],80:[function(e,t,r){"use strict";var n=0,i=1,a=2,o=function(e){return this instanceof o?(this.id="Thenable/1.0.7",this.state=n,this.fulfillValue=void 0,this.rejectReason=void 0,this.onFulfilled=[],this.onRejected=[],this.proxy={then:this.then.bind(this)},void("function"==typeof e&&e.call(this,this.fulfill.bind(this),this.reject.bind(this)))):new o(e)};o.prototype={fulfill:function(e){return s(this,i,"fulfillValue",e)},reject:function(e){return s(this,a,"rejectReason",e)},then:function(e,t){var r=this,n=new o;return r.onFulfilled.push(c(e,n,"fulfill")),r.onRejected.push(c(t,n,"reject")),l(r),n.proxy}};var s=function(e,t,r,i){return e.state===n&&(e.state=t,e[r]=i,l(e)),e},l=function(e){e.state===i?u(e,"onFulfilled",e.fulfillValue):e.state===a&&u(e,"onRejected",e.rejectReason)},u=function(e,t,r){if(0!==e[t].length){var n=e[t];e[t]=[];var i=function(){for(var e=0;e\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]",comparatorOp:"=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=",boolOp:"\\?|\\!|\\^",string:'"(?:\\\\"|[^"])+"|'+"'(?:\\\\'|[^'])+'",number:i.regex.number,meta:"degree|indegree|outdegree",separator:"\\s*,\\s*",descendant:"\\s+",child:"\\s+>\\s+",subject:"\\$"};u.variable="(?:[\\w-]|(?:\\\\"+u.metaChar+"))+",u.value=u.string+"|"+u.number,u.className=u.variable,u.id=u.variable;for(var c=function(e){return e.replace(new RegExp("\\\\("+u.metaChar+")","g"),function(e,t,r,n){return t})},d=u.comparatorOp.split("|"),h=0;h=0||"="!==p&&(u.comparatorOp+="|\\!"+p)}var v=[{name:"group",query:!0,regex:"(node|edge|\\*)",populate:function(e){this.group="*"==e?e:e+"s"}},{name:"state",query:!0,regex:"(:selected|:unselected|:locked|:unlocked|:visible|:hidden|:transparent|:grabbed|:free|:removed|:inside|:grabbable|:ungrabbable|:animated|:unanimated|:selectable|:unselectable|:orphan|:nonorphan|:parent|:child|:loop|:simple|:active|:inactive|:touch|:backgrounding|:nonbackgrounding)",populate:function(e){this.colonSelectors.push(e)}},{name:"id",query:!0,regex:"\\#("+u.id+")",populate:function(e){this.ids.push(c(e))}},{name:"className",query:!0,regex:"\\.("+u.className+")",populate:function(e){this.classes.push(c(e))}},{name:"dataExists",query:!0,regex:"\\[\\s*("+u.variable+")\\s*\\]",populate:function(e){this.data.push({field:c(e)})}},{name:"dataCompare",query:!0,regex:"\\[\\s*("+u.variable+")\\s*("+u.comparatorOp+")\\s*("+u.value+")\\s*\\]",populate:function(e,t,r){var n=null!=new RegExp("^"+u.string+"$").exec(r);r=n?r.substring(1,r.length-1):parseFloat(r),this.data.push({field:c(e),operator:t,value:r})}},{name:"dataBool",query:!0,regex:"\\[\\s*("+u.boolOp+")\\s*("+u.variable+")\\s*\\]",populate:function(e,t){this.data.push({field:c(t),operator:e})}},{name:"metaCompare",query:!0,regex:"\\[\\[\\s*("+u.meta+")\\s*("+u.comparatorOp+")\\s*("+u.number+")\\s*\\]\\]",populate:function(e,t,r){this.meta.push({field:c(e),operator:t,value:parseFloat(r)})}},{name:"nextQuery",separator:!0,regex:u.separator,populate:function(){r[++h]=l(),s=null}},{name:"child",separator:!0,regex:u.child,populate:function(){var e=l();e.parent=this,e.subject=s,r[h]=e}},{name:"descendant",separator:!0,regex:u.descendant,populate:function(){var e=l();e.ancestor=this,e.subject=s,r[h]=e}},{name:"subject",modifier:!0,regex:u.subject,populate:function(){return null!=s&&this.subject!=this?(i.error("Redefinition of subject in selector `"+t+"`"),!1):(s=this,void(this.subject=this))}}];r._private.selectorText=t;var f=t,h=0,g=function(e){for(var t,r,i,a=0;a=0&&(d=d.toLowerCase(),h=h.toLowerCase(),s=s.replace("@",""),p=!0);var v=!1,f=!1;switch(s.indexOf("!")>=0&&(s=s.replace("!",""),v=!0),p&&(l=h.toLowerCase(),c=d.toLowerCase()),s){case"*=":a=d.search(h)>=0;break;case"$=":a=null!=new RegExp(h+"$").exec(d);break;case"^=":a=null!=new RegExp("^"+h).exec(d);break;case"=":a=c===l;break;case"!=":a=c!==l;break;case">":a=v?l>=c:c>l,f=!0;break;case">=":a=v?l>c:c>=l,f=!0;break;case"<":a=v?c>=l:l>c,f=!0;break;case"<=":a=v?c>l:l>=c,f=!0;break;default:a=!1}}else if(null!=s)switch(s){case"?":a=t.fieldTruthy(u);break;case"!":a=!t.fieldTruthy(u);break;case"^":a=t.fieldUndefined(u)}else a=!t.fieldUndefined(u);if(v&&!f&&(a=!a,f=!0),!a){r=!1;break}}return r},v=p({name:"data",fieldValue:function(e){return t._private.data[e]},fieldRef:function(e){return"element._private.data."+e},fieldUndefined:function(e){return void 0===t._private.data[e]},fieldTruthy:function(e){return t._private.data[e]?!0:!1}});if(!v)return!1;var f=p({name:"meta",fieldValue:function(e){return t[e]()},fieldRef:function(e){return"element."+e+"()"},fieldUndefined:function(e){return null==t[e]()},fieldTruthy:function(e){return t[e]()?!0:!1}});if(!f)return!1;if(null!=e.collection){var g=null!=e.collection._private.ids[t.id()];if(!g)return!1}if(null!=e.filter&&0===t.collection().filter(e.filter).size())return!1;var y=function(e,t){if(null!=e){var n=!1;if(!r.hasCompoundNodes())return!1;t=t();for(var i=0;i "+n),null!=e.ancestor&&(n=r(e.ancestor)+" "+n),null!=e.child&&(n+=" > "+r(e.child)),null!=e.descendant&&(n+=" "+r(e.descendant)),n},i=0;i1&&i0;if(h||p){var v;h&&p?v=u.properties:h?v=u.properties:p&&(v=u.mappedProperties);for(var f=0;f0){i=!0;break}}t.hasPie=i;var s=n["text-transform"].strValue,l=n.label.strValue,u=n["font-style"].strValue,o=n["font-size"].pfValue+"px",c=n["font-family"].strValue,d=n["font-weight"].strValue,h=n["text-valign"].strValue,p=n["text-valign"].strValue,v=n["text-outline-width"].pfValue,f=n["text-wrap"].strValue,g=n["text-max-width"].pfValue;t.labelKey=u+"$"+o+"$"+c+"$"+d+"$"+l+"$"+s+"$"+h+"$"+p+"$"+v+"$"+f+"$"+g,t.fontKey=u+"$"+d+"$"+o+"$"+c;var y=n.width.pfValue,m=n.height.pfValue,b=n["border-width"].pfValue;if(t.boundingBoxKey=y+"$"+m+"$"+b,"edges"===e._private.group){var x=n["control-point-step-size"].pfValue,w=n["control-point-distances"]?n["control-point-distances"].pfValue.join("_"):void 0,_=n["control-point-weights"].value.join("_"),E=n["curve-style"].strValue;t.boundingBoxKey+="$"+x+"$"+w+"$"+_+"$"+E}t.styleKey=Date.now()}},a.applyParsedProperty=function(e,t){var r,a,o=this,s=t,l=e._private.style,u=o.types,c=o.properties[s.name].type,d=s.bypass,h=l[s.name],p=h&&h.bypass,v=e._private;if(("height"===t.name||"width"===t.name)&&e.isNode()){if("auto"===t.value&&!e.isParent())return!1;"auto"!==t.value&&e.isParent()&&(s=t=this.parse(t.name,"auto",d))}if(d&&s.deleteBypass){var f=l[s.name];return f?f.bypass&&f.bypassed?(l[s.name]=f.bypassed,!0):!1:!0}var g=function(){n.error("Do not assign mappings to elements without corresponding data (e.g. ele `"+e.id()+"` for property `"+s.name+"` with data field `"+s.field+"`); try a `["+s.field+"]` selector to limit scope to elements with `"+s.field+"` defined")};switch(s.mapped){case u.mapData:case u.mapLayoutData:case u.mapScratch:var r,y=s.mapped===u.mapLayoutData,m=s.mapped===u.mapScratch,b=s.field.split(".");r=m||y?v.scratch:v.data;for(var x=0;x_?_=0:_>1&&(_=1),c.color){var E=s.valueMin[0],D=s.valueMax[0],S=s.valueMin[1],k=s.valueMax[1],T=s.valueMin[2],P=s.valueMax[2],C=null==s.valueMin[3]?1:s.valueMin[3],N=null==s.valueMax[3]?1:s.valueMax[3],M=[Math.round(E+(D-E)*_),Math.round(S+(k-S)*_),Math.round(T+(P-T)*_),Math.round(C+(N-C)*_)];a={bypass:s.bypass,name:s.name,value:M,strValue:"rgb("+M[0]+", "+M[1]+", "+M[2]+")"}}else{if(!c.number)return!1;var B=s.valueMin+(s.valueMax-s.valueMin)*_;a=this.parse(s.name,B,s.bypass,!0)}a||(a=this.parse(s.name,h.strValue,s.bypass,!0)),a||g(),a.mapping=s,s=a;break;case u.data:case u.layoutData:case u.scratch:var r,y=s.mapped===u.layoutData,m=s.mapped===u.scratch,b=s.field.split(".");if(r=m||y?v.scratch:v.data)for(var x=0;x0&&l>0){for(var d=!1,h=0;h0&&e.delay(u),e.animate({css:c},{duration:l,easing:o["transition-timing-function"].value,queue:!1,complete:function(){r||n.removeBypasses(e,s),a.transitioning=!1}})}else a.transitioning&&(e.stop(),this.removeBypasses(e,s),a.transitioning=!1)},t.exports=a},{"../is":77,"../util":94}],83:[function(e,t,r){"use strict";var n=e("../is"),i=e("../util"),a={};a.applyBypass=function(e,t,r,a){var o=this,s=[],l=!0;if("*"===t||"**"===t){if(void 0!==r)for(var u=0;ud.max)return null;var M={name:e,value:t,strValue:""+t+(T?T:""),units:T,bypass:r};return d.unitless||"px"!==T&&"em"!==T?M.pfValue=t:M.pfValue="px"!==T&&T?this.getEmSizeInPixels()*t:t,("ms"===T||"s"===T)&&(M.pfValue="ms"===T?t:1e3*t),("deg"===T||"rad"===T)&&(M.pfValue="rad"===T?t:t*Math.PI/180),M}if(d.propList){var B=[],z=""+t;if("none"===z);else{for(var O=z.split(","),I=0;I node").css({width:"auto",height:"auto",shape:"rectangle","padding-top":10,"padding-right":10,"padding-left":10,"padding-bottom":10}).selector("edge").css({width:1}).selector(":active").css({"overlay-color":"black","overlay-padding":10,"overlay-opacity":.25}).selector("core").css({"selection-box-color":"#ddd","selection-box-opacity":.65,"selection-box-border-color":"#aaa","selection-box-border-width":1,"active-bg-color":"black","active-bg-opacity":.15,"active-bg-size":30,"outside-texture-bg-color":"#000","outside-texture-bg-opacity":.125}),this.defaultLength=this.length},t.exports=i},{"../util":94}],90:[function(e,t,r){"use strict";var n=e("../util"),i=e("../selector"),a={};a.applyFromString=function(e){function t(){c=c.length>a.length?c.substr(a.length):""}function r(){o=o.length>s.length?o.substr(s.length):""}var a,o,s,l=this,u=this,c=""+e;for(c=c.replace(/[\/][*](\s|.)+?[*][\/]/g,"");;){var d=c.match(/^\s*$/);if(d)break;var h=c.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);if(!h){n.error("Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: "+c);break}a=h[0];var p=h[1];if("core"!==p){var v=new i(p);if(v._private.invalid){n.error("Skipping parsing of block: Invalid selector found in string stylesheet: "+p),t();continue}}var f=h[2],g=!1;o=f;for(var y=[];;){var d=o.match(/^\s*$/);if(d)break;var m=o.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);if(!m){n.error("Skipping parsing of block: Invalid formatting of style property and value definitions found in:"+f),g=!0;break}s=m[0];var b=m[1],x=m[2],w=l.properties[b];if(w){var _=u.parse(b,x);_?(y.push({name:b,val:x}),r()):(n.error("Skipping property: Invalid property definition in: "+s),r())}else n.error("Skipping property: Invalid property name in: "+s),r()}if(g){t();break}u.selector(p);for(var E=0;E1?", "+JSON.stringify(r):"")+" );"," "," resolve = origResolve;"," resolve( res.length > 0 ? res : ret );","}"].join("\n"))}};util.extend(thdfn,{reduce:defineFnal({name:"reduce"}),reduceRight:defineFnal({name:"reduceRight"}),map:defineFnal({name:"map"})});var fn=thdfn;fn.promise=fn.run,fn.terminate=fn.halt=fn.stop,fn.include=fn.require,util.extend(thdfn,{on:define.on(),one:define.on({unbindSelfOnTrigger:!0}),off:define.off(),trigger:define.trigger()}),define.eventAliasesOn(thdfn),module.exports=Thread},{"./define":41,"./event":42,"./is":77,"./promise":80,"./util":94,"./window":100,child_process:void 0,path:void 0}],93:[function(e,t,r){"use strict";var n=e("../is");t.exports={hex2tuple:function(e){if((4===e.length||7===e.length)&&"#"===e[0]){var t,r,n,i=4===e.length,a=16;return i?(t=parseInt(e[1]+e[1],a),r=parseInt(e[2]+e[2],a),n=parseInt(e[3]+e[3],a)):(t=parseInt(e[1]+e[2],a),r=parseInt(e[3]+e[4],a),n=parseInt(e[5]+e[6],a)),[t,r,n]}},hsl2tuple:function(e){function t(e,t,r){return 0>r&&(r+=1),r>1&&(r-=1),1/6>r?e+6*(t-e)*r:.5>r?t:2/3>r?e+(t-e)*(2/3-r)*6:e}var r,n,i,a,o,s,l,u,c=new RegExp("^"+this.regex.hsla+"$").exec(e);if(c){if(n=parseInt(c[1]),0>n?n=(360- -1*n%360)%360:n>360&&(n%=360),n/=360,i=parseFloat(c[2]),0>i||i>100)return;if(i/=100,a=parseFloat(c[3]),0>a||a>100)return;if(a/=100,o=c[4],void 0!==o&&(o=parseFloat(o),0>o||o>1))return;if(0===i)s=l=u=Math.round(255*a);else{var d=.5>a?a*(1+i):a+i-a*i,h=2*a-d;s=Math.round(255*t(h,d,n+1/3)),l=Math.round(255*t(h,d,n)),u=Math.round(255*t(h,d,n-1/3))}r=[s,l,u,o]}return r},rgb2tuple:function(e){var t,r=new RegExp("^"+this.regex.rgba+"$").exec(e);if(r){t=[];for(var n=[],i=1;3>=i;i++){var a=r[i];if("%"===a[a.length-1]&&(n[i]=!0),a=parseFloat(a),n[i]&&(a=a/100*255),0>a||a>255)return;t.push(Math.floor(a))}var o=n[1]||n[2]||n[3],s=n[1]&&n[2]&&n[3];if(o&&!s)return;var l=r[4];if(void 0!==l){if(l=parseFloat(l),0>l||l>1)return;t.push(l)}}return t},colorname2tuple:function(e){return this.colors[e.toLowerCase()]},color2tuple:function(e){return(n.array(e)?e:null)||this.colorname2tuple(e)||this.hex2tuple(e)||this.rgb2tuple(e)||this.hsl2tuple(e)},colors:{transparent:[0,0,0,0],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]}}},{"../is":77}],94:[function(e,t,r){"use strict";var n=e("../is"),i=e("../math"),a={falsify:function(){return!1},zeroify:function(){return 0},noop:function(){},error:function(e){console.error?(console.error.apply(console,arguments),console.trace&&console.trace()):(console.log.apply(console,arguments),console.trace&&console.trace())},clone:function(e){return this.extend({},e)},copy:function(e){return null==e?e:n.array(e)?e.slice():n.plainObject(e)?this.clone(e):e}};a.makeBoundingBox=i.makeBoundingBox.bind(i),a._staticEmptyObject={},a.staticEmptyObject=function(){return a._staticEmptyObject},a.extend=null!=Object.assign?Object.assign:function(e){for(var t=arguments,r=1;ro;o++){var t=i[o];n.plainObject(t)&&this.error("Tried to set map with object key"),oa;a++){var o=r[a];if(n.plainObject(o)&&this.error("Tried to get map with object key"),t=t[o],null==t)return t}return t},deleteMap:function(e){for(var t=e.map,r=e.keys,i=r.length,a=e.keepChildren,o=0;i>o;o++){var s=r[o];n.plainObject(s)&&this.error("Tried to delete map with object key");var l=o===e.keys.length-1;if(l)if(a)for(var u in t)a[u]||(t[u]=void 0);else t[s]=void 0;else t=t[s]}}}},{"../is":77}],96:[function(e,t,r){"use strict";t.exports=function(e,t){var r=this,n={};return t||(t=function(){if(1===arguments.length)return arguments[0];for(var e=[],t=0;t=r){a&&clearTimeout(a);var i=c;a=u=c=void 0,i&&(h=d.now(),o=e.apply(l,n),u||a||(n=l=null))}else u=setTimeout(g,r)},y=function(){u&&clearTimeout(u),a=u=c=void 0,(v||p!==t)&&(h=d.now(),o=e.apply(l,n),u||a||(n=l=null))};return function(){if(n=arguments,s=d.now(),l=this,c=v&&(u||!f),p===!1)var r=f&&!u;else{a||f||(h=s);var i=p-(s-h),m=0>=i;m?(a&&(a=clearTimeout(a)),h=s,o=e.apply(l,n)):a||(a=setTimeout(y,i))}return m&&u?u=clearTimeout(u):u||t===p||(u=setTimeout(g,t)),r&&(m=!0,o=e.apply(l,n)),!m||u||a||(n=l=null),o}}},t.exports=o},{"../is":77,"../window":100}],100:[function(e,t,r){t.exports="undefined"==typeof window?null:window},{}]},{},[76])(76)}); -//# sourceMappingURL=cytoscape.min.js.map diff --git a/docs/htmldoc/js/dagre.js b/docs/htmldoc/js/dagre.js deleted file mode 100644 index 830997b..0000000 --- a/docs/htmldoc/js/dagre.js +++ /dev/null @@ -1,16396 +0,0 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.dagre=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0; --i) { - entry = buckets[i].dequeue(); - if (entry) { - results = results.concat(removeNode(g, buckets, zeroIdx, entry, true)); - break; - } - } - } - } - - return results; -} - -function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) { - var results = collectPredecessors ? [] : undefined; - - _.each(g.inEdges(entry.v), function(edge) { - var weight = g.edge(edge), - uEntry = g.node(edge.v); - - if (collectPredecessors) { - results.push({ v: edge.v, w: edge.w }); - } - - uEntry.out -= weight; - assignBucket(buckets, zeroIdx, uEntry); - }); - - _.each(g.outEdges(entry.v), function(edge) { - var weight = g.edge(edge), - w = edge.w, - wEntry = g.node(w); - wEntry["in"] -= weight; - assignBucket(buckets, zeroIdx, wEntry); - }); - - g.removeNode(entry.v); - - return results; -} - -function buildState(g, weightFn) { - var fasGraph = new Graph(), - maxIn = 0, - maxOut = 0; - - _.each(g.nodes(), function(v) { - fasGraph.setNode(v, { v: v, "in": 0, out: 0 }); - }); - - // Aggregate weights on nodes, but also sum the weights across multi-edges - // into a single edge for the fasGraph. - _.each(g.edges(), function(e) { - var prevWeight = fasGraph.edge(e.v, e.w) || 0, - weight = weightFn(e), - edgeWeight = prevWeight + weight; - fasGraph.setEdge(e.v, e.w, edgeWeight); - maxOut = Math.max(maxOut, fasGraph.node(e.v).out += weight); - maxIn = Math.max(maxIn, fasGraph.node(e.w)["in"] += weight); - }); - - var buckets = _.range(maxOut + maxIn + 3).map(function() { return new List(); }); - var zeroIdx = maxIn + 1; - - _.each(fasGraph.nodes(), function(v) { - assignBucket(buckets, zeroIdx, fasGraph.node(v)); - }); - - return { graph: fasGraph, buckets: buckets, zeroIdx: zeroIdx }; -} - -function assignBucket(buckets, zeroIdx, entry) { - if (!entry.out) { - buckets[0].enqueue(entry); - } else if (!entry["in"]) { - buckets[buckets.length - 1].enqueue(entry); - } else { - buckets[entry.out - entry["in"] + zeroIdx].enqueue(entry); - } -} - -},{"./data/list":5,"./graphlib":7,"./lodash":10}],9:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"), - acyclic = require("./acyclic"), - normalize = require("./normalize"), - rank = require("./rank"), - normalizeRanks = require("./util").normalizeRanks, - parentDummyChains = require("./parent-dummy-chains"), - removeEmptyRanks = require("./util").removeEmptyRanks, - nestingGraph = require("./nesting-graph"), - addBorderSegments = require("./add-border-segments"), - coordinateSystem = require("./coordinate-system"), - order = require("./order"), - position = require("./position"), - util = require("./util"), - Graph = require("./graphlib").Graph; - -module.exports = layout; - -function layout(g, opts) { - var time = opts && opts.debugTiming ? util.time : util.notime; - time("layout", function() { - var layoutGraph = time(" buildLayoutGraph", - function() { return buildLayoutGraph(g); }); - time(" runLayout", function() { runLayout(layoutGraph, time); }); - time(" updateInputGraph", function() { updateInputGraph(g, layoutGraph); }); - }); -} - -function runLayout(g, time) { - time(" makeSpaceForEdgeLabels", function() { makeSpaceForEdgeLabels(g); }); - time(" removeSelfEdges", function() { removeSelfEdges(g); }); - time(" acyclic", function() { acyclic.run(g); }); - time(" nestingGraph.run", function() { nestingGraph.run(g); }); - time(" rank", function() { rank(util.asNonCompoundGraph(g)); }); - time(" injectEdgeLabelProxies", function() { injectEdgeLabelProxies(g); }); - time(" removeEmptyRanks", function() { removeEmptyRanks(g); }); - time(" nestingGraph.cleanup", function() { nestingGraph.cleanup(g); }); - time(" normalizeRanks", function() { normalizeRanks(g); }); - time(" assignRankMinMax", function() { assignRankMinMax(g); }); - time(" removeEdgeLabelProxies", function() { removeEdgeLabelProxies(g); }); - time(" normalize.run", function() { normalize.run(g); }); - time(" parentDummyChains", function() { parentDummyChains(g); }); - time(" addBorderSegments", function() { addBorderSegments(g); }); - time(" order", function() { order(g); }); - time(" insertSelfEdges", function() { insertSelfEdges(g); }); - time(" adjustCoordinateSystem", function() { coordinateSystem.adjust(g); }); - time(" position", function() { position(g); }); - time(" positionSelfEdges", function() { positionSelfEdges(g); }); - time(" removeBorderNodes", function() { removeBorderNodes(g); }); - time(" normalize.undo", function() { normalize.undo(g); }); - time(" fixupEdgeLabelCoords", function() { fixupEdgeLabelCoords(g); }); - time(" undoCoordinateSystem", function() { coordinateSystem.undo(g); }); - time(" translateGraph", function() { translateGraph(g); }); - time(" assignNodeIntersects", function() { assignNodeIntersects(g); }); - time(" reversePoints", function() { reversePointsForReversedEdges(g); }); - time(" acyclic.undo", function() { acyclic.undo(g); }); -} - -/* - * Copies final layout information from the layout graph back to the input - * graph. This process only copies whitelisted attributes from the layout graph - * to the input graph, so it serves as a good place to determine what - * attributes can influence layout. - */ -function updateInputGraph(inputGraph, layoutGraph) { - _.each(inputGraph.nodes(), function(v) { - var inputLabel = inputGraph.node(v), - layoutLabel = layoutGraph.node(v); - - if (inputLabel) { - inputLabel.x = layoutLabel.x; - inputLabel.y = layoutLabel.y; - - if (layoutGraph.children(v).length) { - inputLabel.width = layoutLabel.width; - inputLabel.height = layoutLabel.height; - } - } - }); - - _.each(inputGraph.edges(), function(e) { - var inputLabel = inputGraph.edge(e), - layoutLabel = layoutGraph.edge(e); - - inputLabel.points = layoutLabel.points; - if (_.has(layoutLabel, "x")) { - inputLabel.x = layoutLabel.x; - inputLabel.y = layoutLabel.y; - } - }); - - inputGraph.graph().width = layoutGraph.graph().width; - inputGraph.graph().height = layoutGraph.graph().height; -} - -var graphNumAttrs = ["nodesep", "edgesep", "ranksep", "marginx", "marginy"], - graphDefaults = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: "tb" }, - graphAttrs = ["acyclicer", "ranker", "rankdir", "align"], - nodeNumAttrs = ["width", "height"], - nodeDefaults = { width: 0, height: 0 }, - edgeNumAttrs = ["minlen", "weight", "width", "height", "labeloffset"], - edgeDefaults = { - minlen: 1, weight: 1, width: 0, height: 0, - labeloffset: 10, labelpos: "r" - }, - edgeAttrs = ["labelpos"]; - -/* - * Constructs a new graph from the input graph, which can be used for layout. - * This process copies only whitelisted attributes from the input graph to the - * layout graph. Thus this function serves as a good place to determine what - * attributes can influence layout. - */ -function buildLayoutGraph(inputGraph) { - var g = new Graph({ multigraph: true, compound: true }), - graph = canonicalize(inputGraph.graph()); - - g.setGraph(_.merge({}, - graphDefaults, - selectNumberAttrs(graph, graphNumAttrs), - _.pick(graph, graphAttrs))); - - _.each(inputGraph.nodes(), function(v) { - var node = canonicalize(inputGraph.node(v)); - g.setNode(v, _.defaults(selectNumberAttrs(node, nodeNumAttrs), nodeDefaults)); - g.setParent(v, inputGraph.parent(v)); - }); - - _.each(inputGraph.edges(), function(e) { - var edge = canonicalize(inputGraph.edge(e)); - g.setEdge(e, _.merge({}, - edgeDefaults, - selectNumberAttrs(edge, edgeNumAttrs), - _.pick(edge, edgeAttrs))); - }); - - return g; -} - -/* - * This idea comes from the Gansner paper: to account for edge labels in our - * layout we split each rank in half by doubling minlen and halving ranksep. - * Then we can place labels at these mid-points between nodes. - * - * We also add some minimal padding to the width to push the label for the edge - * away from the edge itself a bit. - */ -function makeSpaceForEdgeLabels(g) { - var graph = g.graph(); - graph.ranksep /= 2; - _.each(g.edges(), function(e) { - var edge = g.edge(e); - edge.minlen *= 2; - if (edge.labelpos.toLowerCase() !== "c") { - if (graph.rankdir === "TB" || graph.rankdir === "BT") { - edge.width += edge.labeloffset; - } else { - edge.height += edge.labeloffset; - } - } - }); -} - -/* - * Creates temporary dummy nodes that capture the rank in which each edge's - * label is going to, if it has one of non-zero width and height. We do this - * so that we can safely remove empty ranks while preserving balance for the - * label's position. - */ -function injectEdgeLabelProxies(g) { - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (edge.width && edge.height) { - var v = g.node(e.v), - w = g.node(e.w), - label = { rank: (w.rank - v.rank) / 2 + v.rank, e: e }; - util.addDummyNode(g, "edge-proxy", label, "_ep"); - } - }); -} - -function assignRankMinMax(g) { - var maxRank = 0; - _.each(g.nodes(), function(v) { - var node = g.node(v); - if (node.borderTop) { - node.minRank = g.node(node.borderTop).rank; - node.maxRank = g.node(node.borderBottom).rank; - maxRank = _.max(maxRank, node.maxRank); - } - }); - g.graph().maxRank = maxRank; -} - -function removeEdgeLabelProxies(g) { - _.each(g.nodes(), function(v) { - var node = g.node(v); - if (node.dummy === "edge-proxy") { - g.edge(node.e).labelRank = node.rank; - g.removeNode(v); - } - }); -} - -function translateGraph(g) { - var minX = Number.POSITIVE_INFINITY, - maxX = 0, - minY = Number.POSITIVE_INFINITY, - maxY = 0, - graphLabel = g.graph(), - marginX = graphLabel.marginx || 0, - marginY = graphLabel.marginy || 0; - - function getExtremes(attrs) { - var x = attrs.x, - y = attrs.y, - w = attrs.width, - h = attrs.height; - minX = Math.min(minX, x - w / 2); - maxX = Math.max(maxX, x + w / 2); - minY = Math.min(minY, y - h / 2); - maxY = Math.max(maxY, y + h / 2); - } - - _.each(g.nodes(), function(v) { getExtremes(g.node(v)); }); - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (_.has(edge, "x")) { - getExtremes(edge); - } - }); - - minX -= marginX; - minY -= marginY; - - _.each(g.nodes(), function(v) { - var node = g.node(v); - node.x -= minX; - node.y -= minY; - }); - - _.each(g.edges(), function(e) { - var edge = g.edge(e); - _.each(edge.points, function(p) { - p.x -= minX; - p.y -= minY; - }); - if (_.has(edge, "x")) { edge.x -= minX; } - if (_.has(edge, "y")) { edge.y -= minY; } - }); - - graphLabel.width = maxX - minX + marginX; - graphLabel.height = maxY - minY + marginY; -} - -function assignNodeIntersects(g) { - _.each(g.edges(), function(e) { - var edge = g.edge(e), - nodeV = g.node(e.v), - nodeW = g.node(e.w), - p1, p2; - if (!edge.points) { - edge.points = []; - p1 = nodeW; - p2 = nodeV; - } else { - p1 = edge.points[0]; - p2 = edge.points[edge.points.length - 1]; - } - edge.points.unshift(util.intersectRect(nodeV, p1)); - edge.points.push(util.intersectRect(nodeW, p2)); - }); -} - -function fixupEdgeLabelCoords(g) { - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (_.has(edge, "x")) { - if (edge.labelpos === "l" || edge.labelpos === "r") { - edge.width -= edge.labeloffset; - } - switch (edge.labelpos) { - case "l": edge.x -= edge.width / 2 + edge.labeloffset; break; - case "r": edge.x += edge.width / 2 + edge.labeloffset; break; - } - } - }); -} - -function reversePointsForReversedEdges(g) { - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (edge.reversed) { - edge.points.reverse(); - } - }); -} - -function removeBorderNodes(g) { - _.each(g.nodes(), function(v) { - if (g.children(v).length) { - var node = g.node(v), - t = g.node(node.borderTop), - b = g.node(node.borderBottom), - l = g.node(_.last(node.borderLeft)), - r = g.node(_.last(node.borderRight)); - - node.width = Math.abs(r.x - l.x); - node.height = Math.abs(b.y - t.y); - node.x = l.x + node.width / 2; - node.y = t.y + node.height / 2; - } - }); - - _.each(g.nodes(), function(v) { - if (g.node(v).dummy === "border") { - g.removeNode(v); - } - }); -} - -function removeSelfEdges(g) { - _.each(g.edges(), function(e) { - if (e.v === e.w) { - var node = g.node(e.v); - if (!node.selfEdges) { - node.selfEdges = []; - } - node.selfEdges.push({ e: e, label: g.edge(e) }); - g.removeEdge(e); - } - }); -} - -function insertSelfEdges(g) { - var layers = util.buildLayerMatrix(g); - _.each(layers, function(layer) { - var orderShift = 0; - _.each(layer, function(v, i) { - var node = g.node(v); - node.order = i + orderShift; - _.each(node.selfEdges, function(selfEdge) { - util.addDummyNode(g, "selfedge", { - width: selfEdge.label.width, - height: selfEdge.label.height, - rank: node.rank, - order: i + (++orderShift), - e: selfEdge.e, - label: selfEdge.label - }, "_se"); - }); - delete node.selfEdges; - }); - }); -} - -function positionSelfEdges(g) { - _.each(g.nodes(), function(v) { - var node = g.node(v); - if (node.dummy === "selfedge") { - var selfNode = g.node(node.e.v), - x = selfNode.x + selfNode.width / 2, - y = selfNode.y, - dx = node.x - x, - dy = selfNode.height / 2; - g.setEdge(node.e, node.label); - g.removeNode(v); - node.label.points = [ - { x: x + 2 * dx / 3, y: y - dy }, - { x: x + 5 * dx / 6, y: y - dy }, - { x: x + dx , y: y }, - { x: x + 5 * dx / 6, y: y + dy }, - { x: x + 2 * dx / 3, y: y + dy }, - ]; - node.label.x = node.x; - node.label.y = node.y; - } - }); -} - -function selectNumberAttrs(obj, attrs) { - return _.mapValues(_.pick(obj, attrs), Number); -} - -function canonicalize(attrs) { - var newAttrs = {}; - _.each(attrs, function(v, k) { - newAttrs[k.toLowerCase()] = v; - }); - return newAttrs; -} - -},{"./acyclic":2,"./add-border-segments":3,"./coordinate-system":4,"./graphlib":7,"./lodash":10,"./nesting-graph":11,"./normalize":12,"./order":17,"./parent-dummy-chains":22,"./position":24,"./rank":26,"./util":29}],10:[function(require,module,exports){ -/* global window */ - -var lodash; - -if (typeof require === "function") { - try { - lodash = require("lodash"); - } catch (e) {} -} - -if (!lodash) { - lodash = window._; -} - -module.exports = lodash; - -},{"lodash":51}],11:[function(require,module,exports){ -var _ = require("./lodash"), - util = require("./util"); - -module.exports = { - run: run, - cleanup: cleanup -}; - -/* - * A nesting graph creates dummy nodes for the tops and bottoms of subgraphs, - * adds appropriate edges to ensure that all cluster nodes are placed between - * these boundries, and ensures that the graph is connected. - * - * In addition we ensure, through the use of the minlen property, that nodes - * and subgraph border nodes to not end up on the same rank. - * - * Preconditions: - * - * 1. Input graph is a DAG - * 2. Nodes in the input graph has a minlen attribute - * - * Postconditions: - * - * 1. Input graph is connected. - * 2. Dummy nodes are added for the tops and bottoms of subgraphs. - * 3. The minlen attribute for nodes is adjusted to ensure nodes do not - * get placed on the same rank as subgraph border nodes. - * - * The nesting graph idea comes from Sander, "Layout of Compound Directed - * Graphs." - */ -function run(g) { - var root = util.addDummyNode(g, "root", {}, "_root"), - depths = treeDepths(g), - height = _.max(depths) - 1, - nodeSep = 2 * height + 1; - - g.graph().nestingRoot = root; - - // Multiply minlen by nodeSep to align nodes on non-border ranks. - _.each(g.edges(), function(e) { g.edge(e).minlen *= nodeSep; }); - - // Calculate a weight that is sufficient to keep subgraphs vertically compact - var weight = sumWeights(g) + 1; - - // Create border nodes and link them up - _.each(g.children(), function(child) { - dfs(g, root, nodeSep, weight, height, depths, child); - }); - - // Save the multiplier for node layers for later removal of empty border - // layers. - g.graph().nodeRankFactor = nodeSep; -} - -function dfs(g, root, nodeSep, weight, height, depths, v) { - var children = g.children(v); - if (!children.length) { - if (v !== root) { - g.setEdge(root, v, { weight: 0, minlen: nodeSep }); - } - return; - } - - var top = util.addBorderNode(g, "_bt"), - bottom = util.addBorderNode(g, "_bb"), - label = g.node(v); - - g.setParent(top, v); - label.borderTop = top; - g.setParent(bottom, v); - label.borderBottom = bottom; - - _.each(children, function(child) { - dfs(g, root, nodeSep, weight, height, depths, child); - - var childNode = g.node(child), - childTop = childNode.borderTop ? childNode.borderTop : child, - childBottom = childNode.borderBottom ? childNode.borderBottom : child, - thisWeight = childNode.borderTop ? weight : 2 * weight, - minlen = childTop !== childBottom ? 1 : height - depths[v] + 1; - - g.setEdge(top, childTop, { - weight: thisWeight, - minlen: minlen, - nestingEdge: true - }); - - g.setEdge(childBottom, bottom, { - weight: thisWeight, - minlen: minlen, - nestingEdge: true - }); - }); - - if (!g.parent(v)) { - g.setEdge(root, top, { weight: 0, minlen: height + depths[v] }); - } -} - -function treeDepths(g) { - var depths = {}; - function dfs(v, depth) { - var children = g.children(v); - if (children && children.length) { - _.each(children, function(child) { - dfs(child, depth + 1); - }); - } - depths[v] = depth; - } - _.each(g.children(), function(v) { dfs(v, 1); }); - return depths; -} - -function sumWeights(g) { - return _.reduce(g.edges(), function(acc, e) { - return acc + g.edge(e).weight; - }, 0); -} - -function cleanup(g) { - var graphLabel = g.graph(); - g.removeNode(graphLabel.nestingRoot); - delete graphLabel.nestingRoot; - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (edge.nestingEdge) { - g.removeEdge(e); - } - }); -} - -},{"./lodash":10,"./util":29}],12:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"), - util = require("./util"); - -module.exports = { - run: run, - undo: undo -}; - -/* - * Breaks any long edges in the graph into short segments that span 1 layer - * each. This operation is undoable with the denormalize function. - * - * Pre-conditions: - * - * 1. The input graph is a DAG. - * 2. Each node in the graph has a "rank" property. - * - * Post-condition: - * - * 1. All edges in the graph have a length of 1. - * 2. Dummy nodes are added where edges have been split into segments. - * 3. The graph is augmented with a "dummyChains" attribute which contains - * the first dummy in each chain of dummy nodes produced. - */ -function run(g) { - g.graph().dummyChains = []; - _.each(g.edges(), function(edge) { normalizeEdge(g, edge); }); -} - -function normalizeEdge(g, e) { - var v = e.v, - vRank = g.node(v).rank, - w = e.w, - wRank = g.node(w).rank, - name = e.name, - edgeLabel = g.edge(e), - labelRank = edgeLabel.labelRank; - - if (wRank === vRank + 1) return; - - g.removeEdge(e); - - var dummy, attrs, i; - for (i = 0, ++vRank; vRank < wRank; ++i, ++vRank) { - edgeLabel.points = []; - attrs = { - width: 0, height: 0, - edgeLabel: edgeLabel, edgeObj: e, - rank: vRank - }; - dummy = util.addDummyNode(g, "edge", attrs, "_d"); - if (vRank === labelRank) { - attrs.width = edgeLabel.width; - attrs.height = edgeLabel.height; - attrs.dummy = "edge-label"; - attrs.labelpos = edgeLabel.labelpos; - } - g.setEdge(v, dummy, { weight: edgeLabel.weight }, name); - if (i === 0) { - g.graph().dummyChains.push(dummy); - } - v = dummy; - } - - g.setEdge(v, w, { weight: edgeLabel.weight }, name); -} - -function undo(g) { - _.each(g.graph().dummyChains, function(v) { - var node = g.node(v), - origLabel = node.edgeLabel, - w; - g.setEdge(node.edgeObj, origLabel); - while (node.dummy) { - w = g.successors(v)[0]; - g.removeNode(v); - origLabel.points.push({ x: node.x, y: node.y }); - if (node.dummy === "edge-label") { - origLabel.x = node.x; - origLabel.y = node.y; - origLabel.width = node.width; - origLabel.height = node.height; - } - v = w; - node = g.node(v); - } - }); -} - -},{"./lodash":10,"./util":29}],13:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = addSubgraphConstraints; - -function addSubgraphConstraints(g, cg, vs) { - var prev = {}, - rootPrev; - - _.each(vs, function(v) { - var child = g.parent(v), - parent, - prevChild; - while (child) { - parent = g.parent(child); - if (parent) { - prevChild = prev[parent]; - prev[parent] = child; - } else { - prevChild = rootPrev; - rootPrev = child; - } - if (prevChild && prevChild !== child) { - cg.setEdge(prevChild, child); - return; - } - child = parent; - } - }); - - /* - function dfs(v) { - var children = v ? g.children(v) : g.children(); - if (children.length) { - var min = Number.POSITIVE_INFINITY, - subgraphs = []; - _.each(children, function(child) { - var childMin = dfs(child); - if (g.children(child).length) { - subgraphs.push({ v: child, order: childMin }); - } - min = Math.min(min, childMin); - }); - _.reduce(_.sortBy(subgraphs, "order"), function(prev, curr) { - cg.setEdge(prev.v, curr.v); - return curr; - }); - return min; - } - return g.node(v).order; - } - dfs(undefined); - */ -} - -},{"../lodash":10}],14:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = barycenter; - -function barycenter(g, movable) { - return _.map(movable, function(v) { - var inV = g.inEdges(v); - if (!inV.length) { - return { v: v }; - } else { - var result = _.reduce(inV, function(acc, e) { - var edge = g.edge(e), - nodeU = g.node(e.v); - return { - sum: acc.sum + (edge.weight * nodeU.order), - weight: acc.weight + edge.weight - }; - }, { sum: 0, weight: 0 }); - - return { - v: v, - barycenter: result.sum / result.weight, - weight: result.weight - }; - } - }); -} - - -},{"../lodash":10}],15:[function(require,module,exports){ -var _ = require("../lodash"), - Graph = require("../graphlib").Graph; - -module.exports = buildLayerGraph; - -/* - * Constructs a graph that can be used to sort a layer of nodes. The graph will - * contain all base and subgraph nodes from the request layer in their original - * hierarchy and any edges that are incident on these nodes and are of the type - * requested by the "relationship" parameter. - * - * Nodes from the requested rank that do not have parents are assigned a root - * node in the output graph, which is set in the root graph attribute. This - * makes it easy to walk the hierarchy of movable nodes during ordering. - * - * Pre-conditions: - * - * 1. Input graph is a DAG - * 2. Base nodes in the input graph have a rank attribute - * 3. Subgraph nodes in the input graph has minRank and maxRank attributes - * 4. Edges have an assigned weight - * - * Post-conditions: - * - * 1. Output graph has all nodes in the movable rank with preserved - * hierarchy. - * 2. Root nodes in the movable layer are made children of the node - * indicated by the root attribute of the graph. - * 3. Non-movable nodes incident on movable nodes, selected by the - * relationship parameter, are included in the graph (without hierarchy). - * 4. Edges incident on movable nodes, selected by the relationship - * parameter, are added to the output graph. - * 5. The weights for copied edges are aggregated as need, since the output - * graph is not a multi-graph. - */ -function buildLayerGraph(g, rank, relationship) { - var root = createRootNode(g), - result = new Graph({ compound: true }).setGraph({ root: root }) - .setDefaultNodeLabel(function(v) { return g.node(v); }); - - _.each(g.nodes(), function(v) { - var node = g.node(v), - parent = g.parent(v); - - if (node.rank === rank || node.minRank <= rank && rank <= node.maxRank) { - result.setNode(v); - result.setParent(v, parent || root); - - // This assumes we have only short edges! - _.each(g[relationship](v), function(e) { - var u = e.v === v ? e.w : e.v, - edge = result.edge(u, v), - weight = !_.isUndefined(edge) ? edge.weight : 0; - result.setEdge(u, v, { weight: g.edge(e).weight + weight }); - }); - - if (_.has(node, "minRank")) { - result.setNode(v, { - borderLeft: node.borderLeft[rank], - borderRight: node.borderRight[rank] - }); - } - } - }); - - return result; -} - -function createRootNode(g) { - var v; - while (g.hasNode((v = _.uniqueId("_root")))); - return v; -} - -},{"../graphlib":7,"../lodash":10}],16:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"); - -module.exports = crossCount; - -/* - * A function that takes a layering (an array of layers, each with an array of - * ordererd nodes) and a graph and returns a weighted crossing count. - * - * Pre-conditions: - * - * 1. Input graph must be simple (not a multigraph), directed, and include - * only simple edges. - * 2. Edges in the input graph must have assigned weights. - * - * Post-conditions: - * - * 1. The graph and layering matrix are left unchanged. - * - * This algorithm is derived from Barth, et al., "Bilayer Cross Counting." - */ -function crossCount(g, layering) { - var cc = 0; - for (var i = 1; i < layering.length; ++i) { - cc += twoLayerCrossCount(g, layering[i-1], layering[i]); - } - return cc; -} - -function twoLayerCrossCount(g, northLayer, southLayer) { - // Sort all of the edges between the north and south layers by their position - // in the north layer and then the south. Map these edges to the position of - // their head in the south layer. - var southPos = _.zipObject(southLayer, - _.map(southLayer, function (v, i) { return i; })); - var southEntries = _.flatten(_.map(northLayer, function(v) { - return _.chain(g.outEdges(v)) - .map(function(e) { - return { pos: southPos[e.w], weight: g.edge(e).weight }; - }) - .sortBy("pos") - .value(); - }), true); - - // Build the accumulator tree - var firstIndex = 1; - while (firstIndex < southLayer.length) firstIndex <<= 1; - var treeSize = 2 * firstIndex - 1; - firstIndex -= 1; - var tree = _.map(new Array(treeSize), function() { return 0; }); - - // Calculate the weighted crossings - var cc = 0; - _.each(southEntries.forEach(function(entry) { - var index = entry.pos + firstIndex; - tree[index] += entry.weight; - var weightSum = 0; - while (index > 0) { - if (index % 2) { - weightSum += tree[index + 1]; - } - index = (index - 1) >> 1; - tree[index] += entry.weight; - } - cc += entry.weight * weightSum; - })); - - return cc; -} - -},{"../lodash":10}],17:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - initOrder = require("./init-order"), - crossCount = require("./cross-count"), - sortSubgraph = require("./sort-subgraph"), - buildLayerGraph = require("./build-layer-graph"), - addSubgraphConstraints = require("./add-subgraph-constraints"), - Graph = require("../graphlib").Graph, - util = require("../util"); - -module.exports = order; - -/* - * Applies heuristics to minimize edge crossings in the graph and sets the best - * order solution as an order attribute on each node. - * - * Pre-conditions: - * - * 1. Graph must be DAG - * 2. Graph nodes must be objects with a "rank" attribute - * 3. Graph edges must have the "weight" attribute - * - * Post-conditions: - * - * 1. Graph nodes will have an "order" attribute based on the results of the - * algorithm. - */ -function order(g) { - var maxRank = util.maxRank(g), - downLayerGraphs = buildLayerGraphs(g, _.range(1, maxRank + 1), "inEdges"), - upLayerGraphs = buildLayerGraphs(g, _.range(maxRank - 1, -1, -1), "outEdges"); - - var layering = initOrder(g); - assignOrder(g, layering); - - var bestCC = Number.POSITIVE_INFINITY, - best; - - for (var i = 0, lastBest = 0; lastBest < 4; ++i, ++lastBest) { - sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2); - - layering = util.buildLayerMatrix(g); - var cc = crossCount(g, layering); - if (cc < bestCC) { - lastBest = 0; - best = _.cloneDeep(layering); - bestCC = cc; - } - } - - assignOrder(g, best); -} - -function buildLayerGraphs(g, ranks, relationship) { - return _.map(ranks, function(rank) { - return buildLayerGraph(g, rank, relationship); - }); -} - -function sweepLayerGraphs(layerGraphs, biasRight) { - var cg = new Graph(); - _.each(layerGraphs, function(lg) { - var root = lg.graph().root; - var sorted = sortSubgraph(lg, root, cg, biasRight); - _.each(sorted.vs, function(v, i) { - lg.node(v).order = i; - }); - addSubgraphConstraints(lg, cg, sorted.vs); - }); -} - -function assignOrder(g, layering) { - _.each(layering, function(layer) { - _.each(layer, function(v, i) { - g.node(v).order = i; - }); - }); -} - -},{"../graphlib":7,"../lodash":10,"../util":29,"./add-subgraph-constraints":13,"./build-layer-graph":15,"./cross-count":16,"./init-order":18,"./sort-subgraph":20}],18:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"); - -module.exports = initOrder; - -/* - * Assigns an initial order value for each node by performing a DFS search - * starting from nodes in the first rank. Nodes are assigned an order in their - * rank as they are first visited. - * - * This approach comes from Gansner, et al., "A Technique for Drawing Directed - * Graphs." - * - * Returns a layering matrix with an array per layer and each layer sorted by - * the order of its nodes. - */ -function initOrder(g) { - var visited = {}, - simpleNodes = _.filter(g.nodes(), function(v) { - return !g.children(v).length; - }), - maxRank = _.max(_.map(simpleNodes, function(v) { return g.node(v).rank; })), - layers = _.map(_.range(maxRank + 1), function() { return []; }); - - function dfs(v) { - if (_.has(visited, v)) return; - visited[v] = true; - var node = g.node(v); - layers[node.rank].push(v); - _.each(g.successors(v), dfs); - } - - var orderedVs = _.sortBy(simpleNodes, function(v) { return g.node(v).rank; }); - _.each(orderedVs, dfs); - - return layers; -} - -},{"../lodash":10}],19:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"); - -module.exports = resolveConflicts; - -/* - * Given a list of entries of the form {v, barycenter, weight} and a - * constraint graph this function will resolve any conflicts between the - * constraint graph and the barycenters for the entries. If the barycenters for - * an entry would violate a constraint in the constraint graph then we coalesce - * the nodes in the conflict into a new node that respects the contraint and - * aggregates barycenter and weight information. - * - * This implementation is based on the description in Forster, "A Fast and - * Simple Hueristic for Constrained Two-Level Crossing Reduction," thought it - * differs in some specific details. - * - * Pre-conditions: - * - * 1. Each entry has the form {v, barycenter, weight}, or if the node has - * no barycenter, then {v}. - * - * Returns: - * - * A new list of entries of the form {vs, i, barycenter, weight}. The list - * `vs` may either be a singleton or it may be an aggregation of nodes - * ordered such that they do not violate constraints from the constraint - * graph. The property `i` is the lowest original index of any of the - * elements in `vs`. - */ -function resolveConflicts(entries, cg) { - var mappedEntries = {}; - _.each(entries, function(entry, i) { - var tmp = mappedEntries[entry.v] = { - indegree: 0, - "in": [], - out: [], - vs: [entry.v], - i: i - }; - if (!_.isUndefined(entry.barycenter)) { - tmp.barycenter = entry.barycenter; - tmp.weight = entry.weight; - } - }); - - _.each(cg.edges(), function(e) { - var entryV = mappedEntries[e.v], - entryW = mappedEntries[e.w]; - if (!_.isUndefined(entryV) && !_.isUndefined(entryW)) { - entryW.indegree++; - entryV.out.push(mappedEntries[e.w]); - } - }); - - var sourceSet = _.filter(mappedEntries, function(entry) { - return !entry.indegree; - }); - - return doResolveConflicts(sourceSet); -} - -function doResolveConflicts(sourceSet) { - var entries = []; - - function handleIn(vEntry) { - return function(uEntry) { - if (uEntry.merged) { - return; - } - if (_.isUndefined(uEntry.barycenter) || - _.isUndefined(vEntry.barycenter) || - uEntry.barycenter >= vEntry.barycenter) { - mergeEntries(vEntry, uEntry); - } - }; - } - - function handleOut(vEntry) { - return function(wEntry) { - wEntry["in"].push(vEntry); - if (--wEntry.indegree === 0) { - sourceSet.push(wEntry); - } - }; - } - - while (sourceSet.length) { - var entry = sourceSet.pop(); - entries.push(entry); - _.each(entry["in"].reverse(), handleIn(entry)); - _.each(entry.out, handleOut(entry)); - } - - return _.chain(entries) - .filter(function(entry) { return !entry.merged; }) - .map(function(entry) { - return _.pick(entry, ["vs", "i", "barycenter", "weight"]); - }) - .value(); -} - -function mergeEntries(target, source) { - var sum = 0, - weight = 0; - - if (target.weight) { - sum += target.barycenter * target.weight; - weight += target.weight; - } - - if (source.weight) { - sum += source.barycenter * source.weight; - weight += source.weight; - } - - target.vs = source.vs.concat(target.vs); - target.barycenter = sum / weight; - target.weight = weight; - target.i = Math.min(source.i, target.i); - source.merged = true; -} - -},{"../lodash":10}],20:[function(require,module,exports){ -var _ = require("../lodash"), - barycenter = require("./barycenter"), - resolveConflicts = require("./resolve-conflicts"), - sort = require("./sort"); - -module.exports = sortSubgraph; - -function sortSubgraph(g, v, cg, biasRight) { - var movable = g.children(v), - node = g.node(v), - bl = node ? node.borderLeft : undefined, - br = node ? node.borderRight: undefined, - subgraphs = {}; - - if (bl) { - movable = _.filter(movable, function(w) { - return w !== bl && w !== br; - }); - } - - var barycenters = barycenter(g, movable); - _.each(barycenters, function(entry) { - if (g.children(entry.v).length) { - var subgraphResult = sortSubgraph(g, entry.v, cg, biasRight); - subgraphs[entry.v] = subgraphResult; - if (_.has(subgraphResult, "barycenter")) { - mergeBarycenters(entry, subgraphResult); - } - } - }); - - var entries = resolveConflicts(barycenters, cg); - expandSubgraphs(entries, subgraphs); - - var result = sort(entries, biasRight); - - if (bl) { - result.vs = _.flatten([bl, result.vs, br], true); - if (g.predecessors(bl).length) { - var blPred = g.node(g.predecessors(bl)[0]), - brPred = g.node(g.predecessors(br)[0]); - if (!_.has(result, "barycenter")) { - result.barycenter = 0; - result.weight = 0; - } - result.barycenter = (result.barycenter * result.weight + - blPred.order + brPred.order) / (result.weight + 2); - result.weight += 2; - } - } - - return result; -} - -function expandSubgraphs(entries, subgraphs) { - _.each(entries, function(entry) { - entry.vs = _.flatten(entry.vs.map(function(v) { - if (subgraphs[v]) { - return subgraphs[v].vs; - } - return v; - }), true); - }); -} - -function mergeBarycenters(target, other) { - if (!_.isUndefined(target.barycenter)) { - target.barycenter = (target.barycenter * target.weight + - other.barycenter * other.weight) / - (target.weight + other.weight); - target.weight += other.weight; - } else { - target.barycenter = other.barycenter; - target.weight = other.weight; - } -} - -},{"../lodash":10,"./barycenter":14,"./resolve-conflicts":19,"./sort":21}],21:[function(require,module,exports){ -var _ = require("../lodash"), - util = require("../util"); - -module.exports = sort; - -function sort(entries, biasRight) { - var parts = util.partition(entries, function(entry) { - return _.has(entry, "barycenter"); - }); - var sortable = parts.lhs, - unsortable = _.sortBy(parts.rhs, function(entry) { return -entry.i; }), - vs = [], - sum = 0, - weight = 0, - vsIndex = 0; - - sortable.sort(compareWithBias(!!biasRight)); - - vsIndex = consumeUnsortable(vs, unsortable, vsIndex); - - _.each(sortable, function (entry) { - vsIndex += entry.vs.length; - vs.push(entry.vs); - sum += entry.barycenter * entry.weight; - weight += entry.weight; - vsIndex = consumeUnsortable(vs, unsortable, vsIndex); - }); - - var result = { vs: _.flatten(vs, true) }; - if (weight) { - result.barycenter = sum / weight; - result.weight = weight; - } - return result; -} - -function consumeUnsortable(vs, unsortable, index) { - var last; - while (unsortable.length && (last = _.last(unsortable)).i <= index) { - unsortable.pop(); - vs.push(last.vs); - index++; - } - return index; -} - -function compareWithBias(bias) { - return function(entryV, entryW) { - if (entryV.barycenter < entryW.barycenter) { - return -1; - } else if (entryV.barycenter > entryW.barycenter) { - return 1; - } - - return !bias ? entryV.i - entryW.i : entryW.i - entryV.i; - }; -} - -},{"../lodash":10,"../util":29}],22:[function(require,module,exports){ -var _ = require("./lodash"); - -module.exports = parentDummyChains; - -function parentDummyChains(g) { - var postorderNums = postorder(g); - - _.each(g.graph().dummyChains, function(v) { - var node = g.node(v), - edgeObj = node.edgeObj, - pathData = findPath(g, postorderNums, edgeObj.v, edgeObj.w), - path = pathData.path, - lca = pathData.lca, - pathIdx = 0, - pathV = path[pathIdx], - ascending = true; - - while (v !== edgeObj.w) { - node = g.node(v); - - if (ascending) { - while ((pathV = path[pathIdx]) !== lca && - g.node(pathV).maxRank < node.rank) { - pathIdx++; - } - - if (pathV === lca) { - ascending = false; - } - } - - if (!ascending) { - while (pathIdx < path.length - 1 && - g.node(pathV = path[pathIdx + 1]).minRank <= node.rank) { - pathIdx++; - } - pathV = path[pathIdx]; - } - - g.setParent(v, pathV); - v = g.successors(v)[0]; - } - }); -} - -// Find a path from v to w through the lowest common ancestor (LCA). Return the -// full path and the LCA. -function findPath(g, postorderNums, v, w) { - var vPath = [], - wPath = [], - low = Math.min(postorderNums[v].low, postorderNums[w].low), - lim = Math.max(postorderNums[v].lim, postorderNums[w].lim), - parent, - lca; - - // Traverse up from v to find the LCA - parent = v; - do { - parent = g.parent(parent); - vPath.push(parent); - } while (parent && - (postorderNums[parent].low > low || lim > postorderNums[parent].lim)); - lca = parent; - - // Traverse from w to LCA - parent = w; - while ((parent = g.parent(parent)) !== lca) { - wPath.push(parent); - } - - return { path: vPath.concat(wPath.reverse()), lca: lca }; -} - -function postorder(g) { - var result = {}, - lim = 0; - - function dfs(v) { - var low = lim; - _.each(g.children(v), dfs); - result[v] = { low: low, lim: lim++ }; - } - _.each(g.children(), dfs); - - return result; -} - -},{"./lodash":10}],23:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - Graph = require("../graphlib").Graph, - util = require("../util"); - -/* - * This module provides coordinate assignment based on Brandes and Köpf, "Fast - * and Simple Horizontal Coordinate Assignment." - */ - -module.exports = { - positionX: positionX, - findType1Conflicts: findType1Conflicts, - findType2Conflicts: findType2Conflicts, - addConflict: addConflict, - hasConflict: hasConflict, - verticalAlignment: verticalAlignment, - horizontalCompaction: horizontalCompaction, - alignCoordinates: alignCoordinates, - findSmallestWidthAlignment: findSmallestWidthAlignment, - balance: balance -}; - -/* - * Marks all edges in the graph with a type-1 conflict with the "type1Conflict" - * property. A type-1 conflict is one where a non-inner segment crosses an - * inner segment. An inner segment is an edge with both incident nodes marked - * with the "dummy" property. - * - * This algorithm scans layer by layer, starting with the second, for type-1 - * conflicts between the current layer and the previous layer. For each layer - * it scans the nodes from left to right until it reaches one that is incident - * on an inner segment. It then scans predecessors to determine if they have - * edges that cross that inner segment. At the end a final scan is done for all - * nodes on the current rank to see if they cross the last visited inner - * segment. - * - * This algorithm (safely) assumes that a dummy node will only be incident on a - * single node in the layers being scanned. - */ -function findType1Conflicts(g, layering) { - var conflicts = {}; - - function visitLayer(prevLayer, layer) { - var - // last visited node in the previous layer that is incident on an inner - // segment. - k0 = 0, - // Tracks the last node in this layer scanned for crossings with a type-1 - // segment. - scanPos = 0, - prevLayerLength = prevLayer.length, - lastNode = _.last(layer); - - _.each(layer, function(v, i) { - var w = findOtherInnerSegmentNode(g, v), - k1 = w ? g.node(w).order : prevLayerLength; - - if (w || v === lastNode) { - _.each(layer.slice(scanPos, i +1), function(scanNode) { - _.each(g.predecessors(scanNode), function(u) { - var uLabel = g.node(u), - uPos = uLabel.order; - if ((uPos < k0 || k1 < uPos) && - !(uLabel.dummy && g.node(scanNode).dummy)) { - addConflict(conflicts, u, scanNode); - } - }); - }); - scanPos = i + 1; - k0 = k1; - } - }); - - return layer; - } - - _.reduce(layering, visitLayer); - return conflicts; -} - -function findType2Conflicts(g, layering) { - var conflicts = {}; - - function scan(south, southPos, southEnd, prevNorthBorder, nextNorthBorder) { - var v; - _.each(_.range(southPos, southEnd), function(i) { - v = south[i]; - if (g.node(v).dummy) { - _.each(g.predecessors(v), function(u) { - var uNode = g.node(u); - if (uNode.dummy && - (uNode.order < prevNorthBorder || uNode.order > nextNorthBorder)) { - addConflict(conflicts, u, v); - } - }); - } - }); - } - - - function visitLayer(north, south) { - var prevNorthPos = -1, - nextNorthPos, - southPos = 0; - - _.each(south, function(v, southLookahead) { - if (g.node(v).dummy === "border") { - var predecessors = g.predecessors(v); - if (predecessors.length) { - nextNorthPos = g.node(predecessors[0]).order; - scan(south, southPos, southLookahead, prevNorthPos, nextNorthPos); - southPos = southLookahead; - prevNorthPos = nextNorthPos; - } - } - scan(south, southPos, south.length, nextNorthPos, north.length); - }); - - return south; - } - - _.reduce(layering, visitLayer); - return conflicts; -} - -function findOtherInnerSegmentNode(g, v) { - if (g.node(v).dummy) { - return _.find(g.predecessors(v), function(u) { - return g.node(u).dummy; - }); - } -} - -function addConflict(conflicts, v, w) { - if (v > w) { - var tmp = v; - v = w; - w = tmp; - } - - var conflictsV = conflicts[v]; - if (!conflictsV) { - conflicts[v] = conflictsV = {}; - } - conflictsV[w] = true; -} - -function hasConflict(conflicts, v, w) { - if (v > w) { - var tmp = v; - v = w; - w = tmp; - } - return _.has(conflicts[v], w); -} - -/* - * Try to align nodes into vertical "blocks" where possible. This algorithm - * attempts to align a node with one of its median neighbors. If the edge - * connecting a neighbor is a type-1 conflict then we ignore that possibility. - * If a previous node has already formed a block with a node after the node - * we're trying to form a block with, we also ignore that possibility - our - * blocks would be split in that scenario. - */ -function verticalAlignment(g, layering, conflicts, neighborFn) { - var root = {}, - align = {}, - pos = {}; - - // We cache the position here based on the layering because the graph and - // layering may be out of sync. The layering matrix is manipulated to - // generate different extreme alignments. - _.each(layering, function(layer) { - _.each(layer, function(v, order) { - root[v] = v; - align[v] = v; - pos[v] = order; - }); - }); - - _.each(layering, function(layer) { - var prevIdx = -1; - _.each(layer, function(v) { - var ws = neighborFn(v); - if (ws.length) { - ws = _.sortBy(ws, function(w) { return pos[w]; }); - var mp = (ws.length - 1) / 2; - for (var i = Math.floor(mp), il = Math.ceil(mp); i <= il; ++i) { - var w = ws[i]; - if (align[v] === v && - prevIdx < pos[w] && - !hasConflict(conflicts, v, w)) { - align[w] = v; - align[v] = root[v] = root[w]; - prevIdx = pos[w]; - } - } - } - }); - }); - - return { root: root, align: align }; -} - -function horizontalCompaction(g, layering, root, align, reverseSep) { - // This portion of the algorithm differs from BK due to a number of problems. - // Instead of their algorithm we construct a new block graph and do two - // sweeps. The first sweep places blocks with the smallest possible - // coordinates. The second sweep removes unused space by moving blocks to the - // greatest coordinates without violating separation. - var xs = {}, - blockG = buildBlockGraph(g, layering, root, reverseSep); - - // First pass, assign smallest coordinates via DFS - var visited = {}; - function pass1(v) { - if (!_.has(visited, v)) { - visited[v] = true; - xs[v] = _.reduce(blockG.inEdges(v), function(max, e) { - pass1(e.v); - return Math.max(max, xs[e.v] + blockG.edge(e)); - }, 0); - } - } - _.each(blockG.nodes(), pass1); - - var borderType = reverseSep ? "borderLeft" : "borderRight"; - function pass2(v) { - if (visited[v] !== 2) { - visited[v]++; - var node = g.node(v); - var min = _.reduce(blockG.outEdges(v), function(min, e) { - pass2(e.w); - return Math.min(min, xs[e.w] - blockG.edge(e)); - }, Number.POSITIVE_INFINITY); - if (min !== Number.POSITIVE_INFINITY && node.borderType !== borderType) { - xs[v] = Math.max(xs[v], min); - } - } - } - _.each(blockG.nodes(), pass2); - - // Assign x coordinates to all nodes - _.each(align, function(v) { - xs[v] = xs[root[v]]; - }); - - return xs; -} - - -function buildBlockGraph(g, layering, root, reverseSep) { - var blockGraph = new Graph(), - graphLabel = g.graph(), - sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep); - - _.each(layering, function(layer) { - var u; - _.each(layer, function(v) { - var vRoot = root[v]; - blockGraph.setNode(vRoot); - if (u) { - var uRoot = root[u], - prevMax = blockGraph.edge(uRoot, vRoot); - blockGraph.setEdge(uRoot, vRoot, Math.max(sepFn(g, v, u), prevMax || 0)); - } - u = v; - }); - }); - - return blockGraph; -} - -/* - * Returns the alignment that has the smallest width of the given alignments. - */ -function findSmallestWidthAlignment(g, xss) { - return _.min(xss, function(xs) { - var min = _.min(xs, function(x, v) { return x - width(g, v) / 2; }), - max = _.max(xs, function(x, v) { return x + width(g, v) / 2; }); - return max - min; - }); -} - -/* - * Align the coordinates of each of the layout alignments such that - * left-biased alignments have their minimum coordinate at the same point as - * the minimum coordinate of the smallest width alignment and right-biased - * alignments have their maximum coordinate at the same point as the maximum - * coordinate of the smallest width alignment. - */ -function alignCoordinates(xss, alignTo) { - var alignToMin = _.min(alignTo), - alignToMax = _.max(alignTo); - - _.each(["u", "d"], function(vert) { - _.each(["l", "r"], function(horiz) { - var alignment = vert + horiz, - xs = xss[alignment], - delta; - if (xs === alignTo) return; - - delta = horiz === "l" ? alignToMin - _.min(xs) : alignToMax - _.max(xs); - - if (delta) { - xss[alignment] = _.mapValues(xs, function(x) { return x + delta; }); - } - }); - }); -} - -function balance(xss, align) { - return _.mapValues(xss.ul, function(ignore, v) { - if (align) { - return xss[align.toLowerCase()][v]; - } else { - var xs = _.sortBy(_.pluck(xss, v)); - return (xs[1] + xs[2]) / 2; - } - }); -} - -function positionX(g) { - var layering = util.buildLayerMatrix(g), - conflicts = _.merge(findType1Conflicts(g, layering), - findType2Conflicts(g, layering)); - - var xss = {}, - adjustedLayering; - _.each(["u", "d"], function(vert) { - adjustedLayering = vert === "u" ? layering : _.values(layering).reverse(); - _.each(["l", "r"], function(horiz) { - if (horiz === "r") { - adjustedLayering = _.map(adjustedLayering, function(inner) { - return _.values(inner).reverse(); - }); - } - - var neighborFn = _.bind(vert === "u" ? g.predecessors : g.successors, g); - var align = verticalAlignment(g, adjustedLayering, conflicts, neighborFn); - var xs = horizontalCompaction(g, adjustedLayering, - align.root, align.align, - horiz === "r"); - if (horiz === "r") { - xs = _.mapValues(xs, function(x) { return -x; }); - } - xss[vert + horiz] = xs; - }); - }); - - var smallestWidth = findSmallestWidthAlignment(g, xss); - alignCoordinates(xss, smallestWidth); - return balance(xss, g.graph().align); -} - -function sep(nodeSep, edgeSep, reverseSep) { - return function(g, v, w) { - var vLabel = g.node(v), - wLabel = g.node(w), - sum = 0, - delta; - - sum += vLabel.width / 2; - if (_.has(vLabel, "labelpos")) { - switch (vLabel.labelpos.toLowerCase()) { - case "l": delta = -vLabel.width / 2; break; - case "r": delta = vLabel.width / 2; break; - } - } - if (delta) { - sum += reverseSep ? delta : -delta; - } - delta = 0; - - sum += (vLabel.dummy ? edgeSep : nodeSep) / 2; - sum += (wLabel.dummy ? edgeSep : nodeSep) / 2; - - sum += wLabel.width / 2; - if (_.has(wLabel, "labelpos")) { - switch (wLabel.labelpos.toLowerCase()) { - case "l": delta = wLabel.width / 2; break; - case "r": delta = -wLabel.width / 2; break; - } - } - if (delta) { - sum += reverseSep ? delta : -delta; - } - delta = 0; - - return sum; - }; -} - -function width(g, v) { - return g.node(v).width; -} - -},{"../graphlib":7,"../lodash":10,"../util":29}],24:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - util = require("../util"), - positionX = require("./bk").positionX; - -module.exports = position; - -function position(g) { - g = util.asNonCompoundGraph(g); - - positionY(g); - _.each(positionX(g), function(x, v) { - g.node(v).x = x; - }); -} - -function positionY(g) { - var layering = util.buildLayerMatrix(g), - rankSep = g.graph().ranksep, - prevY = 0; - _.each(layering, function(layer) { - var maxHeight = _.max(_.map(layer, function(v) { return g.node(v).height; })); - _.each(layer, function(v) { - g.node(v).y = prevY + maxHeight / 2; - }); - prevY += maxHeight + rankSep; - }); -} - - -},{"../lodash":10,"../util":29,"./bk":23}],25:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - Graph = require("../graphlib").Graph, - slack = require("./util").slack; - -module.exports = feasibleTree; - -/* - * Constructs a spanning tree with tight edges and adjusted the input node's - * ranks to achieve this. A tight edge is one that is has a length that matches - * its "minlen" attribute. - * - * The basic structure for this function is derived from Gansner, et al., "A - * Technique for Drawing Directed Graphs." - * - * Pre-conditions: - * - * 1. Graph must be a DAG. - * 2. Graph must be connected. - * 3. Graph must have at least one node. - * 5. Graph nodes must have been previously assigned a "rank" property that - * respects the "minlen" property of incident edges. - * 6. Graph edges must have a "minlen" property. - * - * Post-conditions: - * - * - Graph nodes will have their rank adjusted to ensure that all edges are - * tight. - * - * Returns a tree (undirected graph) that is constructed using only "tight" - * edges. - */ -function feasibleTree(g) { - var t = new Graph({ directed: false }); - - // Choose arbitrary node from which to start our tree - var start = g.nodes()[0], - size = g.nodeCount(); - t.setNode(start, {}); - - var edge, delta; - while (tightTree(t, g) < size) { - edge = findMinSlackEdge(t, g); - delta = t.hasNode(edge.v) ? slack(g, edge) : -slack(g, edge); - shiftRanks(t, g, delta); - } - - return t; -} - -/* - * Finds a maximal tree of tight edges and returns the number of nodes in the - * tree. - */ -function tightTree(t, g) { - function dfs(v) { - _.each(g.nodeEdges(v), function(e) { - var edgeV = e.v, - w = (v === edgeV) ? e.w : edgeV; - if (!t.hasNode(w) && !slack(g, e)) { - t.setNode(w, {}); - t.setEdge(v, w, {}); - dfs(w); - } - }); - } - - _.each(t.nodes(), dfs); - return t.nodeCount(); -} - -/* - * Finds the edge with the smallest slack that is incident on tree and returns - * it. - */ -function findMinSlackEdge(t, g) { - return _.min(g.edges(), function(e) { - if (t.hasNode(e.v) !== t.hasNode(e.w)) { - return slack(g, e); - } - }); -} - -function shiftRanks(t, g, delta) { - _.each(t.nodes(), function(v) { - g.node(v).rank += delta; - }); -} - -},{"../graphlib":7,"../lodash":10,"./util":28}],26:[function(require,module,exports){ -"use strict"; - -var rankUtil = require("./util"), - longestPath = rankUtil.longestPath, - feasibleTree = require("./feasible-tree"), - networkSimplex = require("./network-simplex"); - -module.exports = rank; - -/* - * Assigns a rank to each node in the input graph that respects the "minlen" - * constraint specified on edges between nodes. - * - * This basic structure is derived from Gansner, et al., "A Technique for - * Drawing Directed Graphs." - * - * Pre-conditions: - * - * 1. Graph must be a connected DAG - * 2. Graph nodes must be objects - * 3. Graph edges must have "weight" and "minlen" attributes - * - * Post-conditions: - * - * 1. Graph nodes will have a "rank" attribute based on the results of the - * algorithm. Ranks can start at any index (including negative), we'll - * fix them up later. - */ -function rank(g) { - switch(g.graph().ranker) { - case "network-simplex": networkSimplexRanker(g); break; - case "tight-tree": tightTreeRanker(g); break; - case "longest-path": longestPathRanker(g); break; - default: networkSimplexRanker(g); - } -} - -// A fast and simple ranker, but results are far from optimal. -var longestPathRanker = longestPath; - -function tightTreeRanker(g) { - longestPath(g); - feasibleTree(g); -} - -function networkSimplexRanker(g) { - networkSimplex(g); -} - -},{"./feasible-tree":25,"./network-simplex":27,"./util":28}],27:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - feasibleTree = require("./feasible-tree"), - slack = require("./util").slack, - initRank = require("./util").longestPath, - preorder = require("../graphlib").alg.preorder, - postorder = require("../graphlib").alg.postorder, - simplify = require("../util").simplify; - -module.exports = networkSimplex; - -// Expose some internals for testing purposes -networkSimplex.initLowLimValues = initLowLimValues; -networkSimplex.initCutValues = initCutValues; -networkSimplex.calcCutValue = calcCutValue; -networkSimplex.leaveEdge = leaveEdge; -networkSimplex.enterEdge = enterEdge; -networkSimplex.exchangeEdges = exchangeEdges; - -/* - * The network simplex algorithm assigns ranks to each node in the input graph - * and iteratively improves the ranking to reduce the length of edges. - * - * Preconditions: - * - * 1. The input graph must be a DAG. - * 2. All nodes in the graph must have an object value. - * 3. All edges in the graph must have "minlen" and "weight" attributes. - * - * Postconditions: - * - * 1. All nodes in the graph will have an assigned "rank" attribute that has - * been optimized by the network simplex algorithm. Ranks start at 0. - * - * - * A rough sketch of the algorithm is as follows: - * - * 1. Assign initial ranks to each node. We use the longest path algorithm, - * which assigns ranks to the lowest position possible. In general this - * leads to very wide bottom ranks and unnecessarily long edges. - * 2. Construct a feasible tight tree. A tight tree is one such that all - * edges in the tree have no slack (difference between length of edge - * and minlen for the edge). This by itself greatly improves the assigned - * rankings by shorting edges. - * 3. Iteratively find edges that have negative cut values. Generally a - * negative cut value indicates that the edge could be removed and a new - * tree edge could be added to produce a more compact graph. - * - * Much of the algorithms here are derived from Gansner, et al., "A Technique - * for Drawing Directed Graphs." The structure of the file roughly follows the - * structure of the overall algorithm. - */ -function networkSimplex(g) { - g = simplify(g); - initRank(g); - var t = feasibleTree(g); - initLowLimValues(t); - initCutValues(t, g); - - var e, f; - while ((e = leaveEdge(t))) { - f = enterEdge(t, g, e); - exchangeEdges(t, g, e, f); - } -} - -/* - * Initializes cut values for all edges in the tree. - */ -function initCutValues(t, g) { - var vs = postorder(t, t.nodes()); - vs = vs.slice(0, vs.length - 1); - _.each(vs, function(v) { - assignCutValue(t, g, v); - }); -} - -function assignCutValue(t, g, child) { - var childLab = t.node(child), - parent = childLab.parent; - t.edge(child, parent).cutvalue = calcCutValue(t, g, child); -} - -/* - * Given the tight tree, its graph, and a child in the graph calculate and - * return the cut value for the edge between the child and its parent. - */ -function calcCutValue(t, g, child) { - var childLab = t.node(child), - parent = childLab.parent, - // True if the child is on the tail end of the edge in the directed graph - childIsTail = true, - // The graph's view of the tree edge we're inspecting - graphEdge = g.edge(child, parent), - // The accumulated cut value for the edge between this node and its parent - cutValue = 0; - - if (!graphEdge) { - childIsTail = false; - graphEdge = g.edge(parent, child); - } - - cutValue = graphEdge.weight; - - _.each(g.nodeEdges(child), function(e) { - var isOutEdge = e.v === child, - other = isOutEdge ? e.w : e.v; - - if (other !== parent) { - var pointsToHead = isOutEdge === childIsTail, - otherWeight = g.edge(e).weight; - - cutValue += pointsToHead ? otherWeight : -otherWeight; - if (isTreeEdge(t, child, other)) { - var otherCutValue = t.edge(child, other).cutvalue; - cutValue += pointsToHead ? -otherCutValue : otherCutValue; - } - } - }); - - return cutValue; -} - -function initLowLimValues(tree, root) { - if (arguments.length < 2) { - root = tree.nodes()[0]; - } - dfsAssignLowLim(tree, {}, 1, root); -} - -function dfsAssignLowLim(tree, visited, nextLim, v, parent) { - var low = nextLim, - label = tree.node(v); - - visited[v] = true; - _.each(tree.neighbors(v), function(w) { - if (!_.has(visited, w)) { - nextLim = dfsAssignLowLim(tree, visited, nextLim, w, v); - } - }); - - label.low = low; - label.lim = nextLim++; - if (parent) { - label.parent = parent; - } else { - // TODO should be able to remove this when we incrementally update low lim - delete label.parent; - } - - return nextLim; -} - -function leaveEdge(tree) { - return _.find(tree.edges(), function(e) { - return tree.edge(e).cutvalue < 0; - }); -} - -function enterEdge(t, g, edge) { - var v = edge.v, - w = edge.w; - - // For the rest of this function we assume that v is the tail and w is the - // head, so if we don't have this edge in the graph we should flip it to - // match the correct orientation. - if (!g.hasEdge(v, w)) { - v = edge.w; - w = edge.v; - } - - var vLabel = t.node(v), - wLabel = t.node(w), - tailLabel = vLabel, - flip = false; - - // If the root is in the tail of the edge then we need to flip the logic that - // checks for the head and tail nodes in the candidates function below. - if (vLabel.lim > wLabel.lim) { - tailLabel = wLabel; - flip = true; - } - - var candidates = _.filter(g.edges(), function(edge) { - return flip === isDescendant(t, t.node(edge.v), tailLabel) && - flip !== isDescendant(t, t.node(edge.w), tailLabel); - }); - - return _.min(candidates, function(edge) { return slack(g, edge); }); -} - -function exchangeEdges(t, g, e, f) { - var v = e.v, - w = e.w; - t.removeEdge(v, w); - t.setEdge(f.v, f.w, {}); - initLowLimValues(t); - initCutValues(t, g); - updateRanks(t, g); -} - -function updateRanks(t, g) { - var root = _.find(t.nodes(), function(v) { return !g.node(v).parent; }), - vs = preorder(t, root); - vs = vs.slice(1); - _.each(vs, function(v) { - var parent = t.node(v).parent, - edge = g.edge(v, parent), - flipped = false; - - if (!edge) { - edge = g.edge(parent, v); - flipped = true; - } - - g.node(v).rank = g.node(parent).rank + (flipped ? edge.minlen : -edge.minlen); - }); -} - -/* - * Returns true if the edge is in the tree. - */ -function isTreeEdge(tree, u, v) { - return tree.hasEdge(u, v); -} - -/* - * Returns true if the specified node is descendant of the root node per the - * assigned low and lim attributes in the tree. - */ -function isDescendant(tree, vLabel, rootLabel) { - return rootLabel.low <= vLabel.lim && vLabel.lim <= rootLabel.lim; -} - -},{"../graphlib":7,"../lodash":10,"../util":29,"./feasible-tree":25,"./util":28}],28:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"); - -module.exports = { - longestPath: longestPath, - slack: slack -}; - -/* - * Initializes ranks for the input graph using the longest path algorithm. This - * algorithm scales well and is fast in practice, it yields rather poor - * solutions. Nodes are pushed to the lowest layer possible, leaving the bottom - * ranks wide and leaving edges longer than necessary. However, due to its - * speed, this algorithm is good for getting an initial ranking that can be fed - * into other algorithms. - * - * This algorithm does not normalize layers because it will be used by other - * algorithms in most cases. If using this algorithm directly, be sure to - * run normalize at the end. - * - * Pre-conditions: - * - * 1. Input graph is a DAG. - * 2. Input graph node labels can be assigned properties. - * - * Post-conditions: - * - * 1. Each node will be assign an (unnormalized) "rank" property. - */ -function longestPath(g) { - var visited = {}; - - function dfs(v) { - var label = g.node(v); - if (_.has(visited, v)) { - return label.rank; - } - visited[v] = true; - - var rank = _.min(_.map(g.outEdges(v), function(e) { - return dfs(e.w) - g.edge(e).minlen; - })); - - if (rank === Number.POSITIVE_INFINITY) { - rank = 0; - } - - return (label.rank = rank); - } - - _.each(g.sources(), dfs); -} - -/* - * Returns the amount of slack for the given edge. The slack is defined as the - * difference between the length of the edge and its minimum length. - */ -function slack(g, e) { - return g.node(e.w).rank - g.node(e.v).rank - g.edge(e).minlen; -} - -},{"../lodash":10}],29:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"), - Graph = require("./graphlib").Graph; - -module.exports = { - addDummyNode: addDummyNode, - simplify: simplify, - asNonCompoundGraph: asNonCompoundGraph, - successorWeights: successorWeights, - predecessorWeights: predecessorWeights, - intersectRect: intersectRect, - buildLayerMatrix: buildLayerMatrix, - normalizeRanks: normalizeRanks, - removeEmptyRanks: removeEmptyRanks, - addBorderNode: addBorderNode, - maxRank: maxRank, - partition: partition, - time: time, - notime: notime -}; - -/* - * Adds a dummy node to the graph and return v. - */ -function addDummyNode(g, type, attrs, name) { - var v; - do { - v = _.uniqueId(name); - } while (g.hasNode(v)); - - attrs.dummy = type; - g.setNode(v, attrs); - return v; -} - -/* - * Returns a new graph with only simple edges. Handles aggregation of data - * associated with multi-edges. - */ -function simplify(g) { - var simplified = new Graph().setGraph(g.graph()); - _.each(g.nodes(), function(v) { simplified.setNode(v, g.node(v)); }); - _.each(g.edges(), function(e) { - var simpleLabel = simplified.edge(e.v, e.w) || { weight: 0, minlen: 1 }, - label = g.edge(e); - simplified.setEdge(e.v, e.w, { - weight: simpleLabel.weight + label.weight, - minlen: Math.max(simpleLabel.minlen, label.minlen) - }); - }); - return simplified; -} - -function asNonCompoundGraph(g) { - var simplified = new Graph({ multigraph: g.isMultigraph() }).setGraph(g.graph()); - _.each(g.nodes(), function(v) { - if (!g.children(v).length) { - simplified.setNode(v, g.node(v)); - } - }); - _.each(g.edges(), function(e) { - simplified.setEdge(e, g.edge(e)); - }); - return simplified; -} - -function successorWeights(g) { - var weightMap = _.map(g.nodes(), function(v) { - var sucs = {}; - _.each(g.outEdges(v), function(e) { - sucs[e.w] = (sucs[e.w] || 0) + g.edge(e).weight; - }); - return sucs; - }); - return _.zipObject(g.nodes(), weightMap); -} - -function predecessorWeights(g) { - var weightMap = _.map(g.nodes(), function(v) { - var preds = {}; - _.each(g.inEdges(v), function(e) { - preds[e.v] = (preds[e.v] || 0) + g.edge(e).weight; - }); - return preds; - }); - return _.zipObject(g.nodes(), weightMap); -} - -/* - * Finds where a line starting at point ({x, y}) would intersect a rectangle - * ({x, y, width, height}) if it were pointing at the rectangle's center. - */ -function intersectRect(rect, point) { - var x = rect.x; - var y = rect.y; - - // Rectangle intersection algorithm from: - // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes - var dx = point.x - x; - var dy = point.y - y; - var w = rect.width / 2; - var h = rect.height / 2; - - if (!dx && !dy) { - throw new Error("Not possible to find intersection inside of the rectangle"); - } - - var sx, sy; - if (Math.abs(dy) * w > Math.abs(dx) * h) { - // Intersection is top or bottom of rect. - if (dy < 0) { - h = -h; - } - sx = h * dx / dy; - sy = h; - } else { - // Intersection is left or right of rect. - if (dx < 0) { - w = -w; - } - sx = w; - sy = w * dy / dx; - } - - return { x: x + sx, y: y + sy }; -} - -/* - * Given a DAG with each node assigned "rank" and "order" properties, this - * function will produce a matrix with the ids of each node. - */ -function buildLayerMatrix(g) { - var layering = _.map(_.range(maxRank(g) + 1), function() { return []; }); - _.each(g.nodes(), function(v) { - var node = g.node(v), - rank = node.rank; - if (!_.isUndefined(rank)) { - layering[rank][node.order] = v; - } - }); - return layering; -} - -/* - * Adjusts the ranks for all nodes in the graph such that all nodes v have - * rank(v) >= 0 and at least one node w has rank(w) = 0. - */ -function normalizeRanks(g) { - var min = _.min(_.map(g.nodes(), function(v) { return g.node(v).rank; })); - _.each(g.nodes(), function(v) { - var node = g.node(v); - if (_.has(node, "rank")) { - node.rank -= min; - } - }); -} - -function removeEmptyRanks(g) { - // Ranks may not start at 0, so we need to offset them - var offset = _.min(_.map(g.nodes(), function(v) { return g.node(v).rank; })); - - var layers = []; - _.each(g.nodes(), function(v) { - var rank = g.node(v).rank - offset; - if (!layers[rank]) { - layers[rank] = []; - } - layers[rank].push(v); - }); - - var delta = 0, - nodeRankFactor = g.graph().nodeRankFactor; - _.each(layers, function(vs, i) { - if (_.isUndefined(vs) && i % nodeRankFactor !== 0) { - --delta; - } else if (delta) { - _.each(vs, function(v) { g.node(v).rank += delta; }); - } - }); -} - -function addBorderNode(g, prefix, rank, order) { - var node = { - width: 0, - height: 0 - }; - if (arguments.length >= 4) { - node.rank = rank; - node.order = order; - } - return addDummyNode(g, "border", node, prefix); -} - -function maxRank(g) { - return _.max(_.map(g.nodes(), function(v) { - var rank = g.node(v).rank; - if (!_.isUndefined(rank)) { - return rank; - } - })); -} - -/* - * Partition a collection into two groups: `lhs` and `rhs`. If the supplied - * function returns true for an entry it goes into `lhs`. Otherwise it goes - * into `rhs. - */ -function partition(collection, fn) { - var result = { lhs: [], rhs: [] }; - _.each(collection, function(value) { - if (fn(value)) { - result.lhs.push(value); - } else { - result.rhs.push(value); - } - }); - return result; -} - -/* - * Returns a new function that wraps `fn` with a timer. The wrapper logs the - * time it takes to execute the function. - */ -function time(name, fn) { - var start = _.now(); - try { - return fn(); - } finally { - console.log(name + " time: " + (_.now() - start) + "ms"); - } -} - -function notime(name, fn) { - return fn(); -} - -},{"./graphlib":7,"./lodash":10}],30:[function(require,module,exports){ -module.exports = "0.7.4"; - -},{}],31:[function(require,module,exports){ -/** - * Copyright (c) 2014, Chris Pettitt - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -var lib = require("./lib"); - -module.exports = { - Graph: lib.Graph, - json: require("./lib/json"), - alg: require("./lib/alg"), - version: lib.version -}; - -},{"./lib":47,"./lib/alg":38,"./lib/json":48}],32:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = components; - -function components(g) { - var visited = {}, - cmpts = [], - cmpt; - - function dfs(v) { - if (_.has(visited, v)) return; - visited[v] = true; - cmpt.push(v); - _.each(g.successors(v), dfs); - _.each(g.predecessors(v), dfs); - } - - _.each(g.nodes(), function(v) { - cmpt = []; - dfs(v); - if (cmpt.length) { - cmpts.push(cmpt); - } - }); - - return cmpts; -} - -},{"../lodash":49}],33:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = dfs; - -/* - * A helper that preforms a pre- or post-order traversal on the input graph - * and returns the nodes in the order they were visited. This algorithm treats - * the input as undirected. - * - * Order must be one of "pre" or "post". - */ -function dfs(g, vs, order) { - if (!_.isArray(vs)) { - vs = [vs]; - } - - var acc = [], - visited = {}; - _.each(vs, function(v) { - if (!g.hasNode(v)) { - throw new Error("Graph does not have node: " + v); - } - - doDfs(g, v, order === "post", visited, acc); - }); - return acc; -} - -function doDfs(g, v, postorder, visited, acc) { - if (!_.has(visited, v)) { - visited[v] = true; - - if (!postorder) { acc.push(v); } - _.each(g.neighbors(v), function(w) { - doDfs(g, w, postorder, visited, acc); - }); - if (postorder) { acc.push(v); } - } -} - -},{"../lodash":49}],34:[function(require,module,exports){ -var dijkstra = require("./dijkstra"), - _ = require("../lodash"); - -module.exports = dijkstraAll; - -function dijkstraAll(g, weightFunc, edgeFunc) { - return _.transform(g.nodes(), function(acc, v) { - acc[v] = dijkstra(g, v, weightFunc, edgeFunc); - }, {}); -} - -},{"../lodash":49,"./dijkstra":35}],35:[function(require,module,exports){ -var _ = require("../lodash"), - PriorityQueue = require("../data/priority-queue"); - -module.exports = dijkstra; - -var DEFAULT_WEIGHT_FUNC = _.constant(1); - -function dijkstra(g, source, weightFn, edgeFn) { - return runDijkstra(g, String(source), - weightFn || DEFAULT_WEIGHT_FUNC, - edgeFn || function(v) { return g.outEdges(v); }); -} - -function runDijkstra(g, source, weightFn, edgeFn) { - var results = {}, - pq = new PriorityQueue(), - v, vEntry; - - var updateNeighbors = function(edge) { - var w = edge.v !== v ? edge.v : edge.w, - wEntry = results[w], - weight = weightFn(edge), - distance = vEntry.distance + weight; - - if (weight < 0) { - throw new Error("dijkstra does not allow negative edge weights. " + - "Bad edge: " + edge + " Weight: " + weight); - } - - if (distance < wEntry.distance) { - wEntry.distance = distance; - wEntry.predecessor = v; - pq.decrease(w, distance); - } - }; - - g.nodes().forEach(function(v) { - var distance = v === source ? 0 : Number.POSITIVE_INFINITY; - results[v] = { distance: distance }; - pq.add(v, distance); - }); - - while (pq.size() > 0) { - v = pq.removeMin(); - vEntry = results[v]; - if (vEntry.distance === Number.POSITIVE_INFINITY) { - break; - } - - edgeFn(v).forEach(updateNeighbors); - } - - return results; -} - -},{"../data/priority-queue":45,"../lodash":49}],36:[function(require,module,exports){ -var _ = require("../lodash"), - tarjan = require("./tarjan"); - -module.exports = findCycles; - -function findCycles(g) { - return _.filter(tarjan(g), function(cmpt) { - return cmpt.length > 1 || (cmpt.length === 1 && g.hasEdge(cmpt[0], cmpt[0])); - }); -} - -},{"../lodash":49,"./tarjan":43}],37:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = floydWarshall; - -var DEFAULT_WEIGHT_FUNC = _.constant(1); - -function floydWarshall(g, weightFn, edgeFn) { - return runFloydWarshall(g, - weightFn || DEFAULT_WEIGHT_FUNC, - edgeFn || function(v) { return g.outEdges(v); }); -} - -function runFloydWarshall(g, weightFn, edgeFn) { - var results = {}, - nodes = g.nodes(); - - nodes.forEach(function(v) { - results[v] = {}; - results[v][v] = { distance: 0 }; - nodes.forEach(function(w) { - if (v !== w) { - results[v][w] = { distance: Number.POSITIVE_INFINITY }; - } - }); - edgeFn(v).forEach(function(edge) { - var w = edge.v === v ? edge.w : edge.v, - d = weightFn(edge); - results[v][w] = { distance: d, predecessor: v }; - }); - }); - - nodes.forEach(function(k) { - var rowK = results[k]; - nodes.forEach(function(i) { - var rowI = results[i]; - nodes.forEach(function(j) { - var ik = rowI[k]; - var kj = rowK[j]; - var ij = rowI[j]; - var altDistance = ik.distance + kj.distance; - if (altDistance < ij.distance) { - ij.distance = altDistance; - ij.predecessor = kj.predecessor; - } - }); - }); - }); - - return results; -} - -},{"../lodash":49}],38:[function(require,module,exports){ -module.exports = { - components: require("./components"), - dijkstra: require("./dijkstra"), - dijkstraAll: require("./dijkstra-all"), - findCycles: require("./find-cycles"), - floydWarshall: require("./floyd-warshall"), - isAcyclic: require("./is-acyclic"), - postorder: require("./postorder"), - preorder: require("./preorder"), - prim: require("./prim"), - tarjan: require("./tarjan"), - topsort: require("./topsort") -}; - -},{"./components":32,"./dijkstra":35,"./dijkstra-all":34,"./find-cycles":36,"./floyd-warshall":37,"./is-acyclic":39,"./postorder":40,"./preorder":41,"./prim":42,"./tarjan":43,"./topsort":44}],39:[function(require,module,exports){ -var topsort = require("./topsort"); - -module.exports = isAcyclic; - -function isAcyclic(g) { - try { - topsort(g); - } catch (e) { - if (e instanceof topsort.CycleException) { - return false; - } - throw e; - } - return true; -} - -},{"./topsort":44}],40:[function(require,module,exports){ -var dfs = require("./dfs"); - -module.exports = postorder; - -function postorder(g, vs) { - return dfs(g, vs, "post"); -} - -},{"./dfs":33}],41:[function(require,module,exports){ -var dfs = require("./dfs"); - -module.exports = preorder; - -function preorder(g, vs) { - return dfs(g, vs, "pre"); -} - -},{"./dfs":33}],42:[function(require,module,exports){ -var _ = require("../lodash"), - Graph = require("../graph"), - PriorityQueue = require("../data/priority-queue"); - -module.exports = prim; - -function prim(g, weightFunc) { - var result = new Graph(), - parents = {}, - pq = new PriorityQueue(), - v; - - function updateNeighbors(edge) { - var w = edge.v === v ? edge.w : edge.v, - pri = pq.priority(w); - if (pri !== undefined) { - var edgeWeight = weightFunc(edge); - if (edgeWeight < pri) { - parents[w] = v; - pq.decrease(w, edgeWeight); - } - } - } - - if (g.nodeCount() === 0) { - return result; - } - - _.each(g.nodes(), function(v) { - pq.add(v, Number.POSITIVE_INFINITY); - result.setNode(v); - }); - - // Start from an arbitrary node - pq.decrease(g.nodes()[0], 0); - - var init = false; - while (pq.size() > 0) { - v = pq.removeMin(); - if (_.has(parents, v)) { - result.setEdge(v, parents[v]); - } else if (init) { - throw new Error("Input graph is not connected: " + g); - } else { - init = true; - } - - g.nodeEdges(v).forEach(updateNeighbors); - } - - return result; -} - -},{"../data/priority-queue":45,"../graph":46,"../lodash":49}],43:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = tarjan; - -function tarjan(g) { - var index = 0, - stack = [], - visited = {}, // node id -> { onStack, lowlink, index } - results = []; - - function dfs(v) { - var entry = visited[v] = { - onStack: true, - lowlink: index, - index: index++ - }; - stack.push(v); - - g.successors(v).forEach(function(w) { - if (!_.has(visited, w)) { - dfs(w); - entry.lowlink = Math.min(entry.lowlink, visited[w].lowlink); - } else if (visited[w].onStack) { - entry.lowlink = Math.min(entry.lowlink, visited[w].index); - } - }); - - if (entry.lowlink === entry.index) { - var cmpt = [], - w; - do { - w = stack.pop(); - visited[w].onStack = false; - cmpt.push(w); - } while (v !== w); - results.push(cmpt); - } - } - - g.nodes().forEach(function(v) { - if (!_.has(visited, v)) { - dfs(v); - } - }); - - return results; -} - -},{"../lodash":49}],44:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = topsort; -topsort.CycleException = CycleException; - -function topsort(g) { - var visited = {}, - stack = {}, - results = []; - - function visit(node) { - if (_.has(stack, node)) { - throw new CycleException(); - } - - if (!_.has(visited, node)) { - stack[node] = true; - visited[node] = true; - _.each(g.predecessors(node), visit); - delete stack[node]; - results.push(node); - } - } - - _.each(g.sinks(), visit); - - if (_.size(visited) !== g.nodeCount()) { - throw new CycleException(); - } - - return results; -} - -function CycleException() {} - -},{"../lodash":49}],45:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = PriorityQueue; - -/** - * A min-priority queue data structure. This algorithm is derived from Cormen, - * et al., "Introduction to Algorithms". The basic idea of a min-priority - * queue is that you can efficiently (in O(1) time) get the smallest key in - * the queue. Adding and removing elements takes O(log n) time. A key can - * have its priority decreased in O(log n) time. - */ -function PriorityQueue() { - this._arr = []; - this._keyIndices = {}; -} - -/** - * Returns the number of elements in the queue. Takes `O(1)` time. - */ -PriorityQueue.prototype.size = function() { - return this._arr.length; -}; - -/** - * Returns the keys that are in the queue. Takes `O(n)` time. - */ -PriorityQueue.prototype.keys = function() { - return this._arr.map(function(x) { return x.key; }); -}; - -/** - * Returns `true` if **key** is in the queue and `false` if not. - */ -PriorityQueue.prototype.has = function(key) { - return _.has(this._keyIndices, key); -}; - -/** - * Returns the priority for **key**. If **key** is not present in the queue - * then this function returns `undefined`. Takes `O(1)` time. - * - * @param {Object} key - */ -PriorityQueue.prototype.priority = function(key) { - var index = this._keyIndices[key]; - if (index !== undefined) { - return this._arr[index].priority; - } -}; - -/** - * Returns the key for the minimum element in this queue. If the queue is - * empty this function throws an Error. Takes `O(1)` time. - */ -PriorityQueue.prototype.min = function() { - if (this.size() === 0) { - throw new Error("Queue underflow"); - } - return this._arr[0].key; -}; - -/** - * Inserts a new key into the priority queue. If the key already exists in - * the queue this function returns `false`; otherwise it will return `true`. - * Takes `O(n)` time. - * - * @param {Object} key the key to add - * @param {Number} priority the initial priority for the key - */ -PriorityQueue.prototype.add = function(key, priority) { - var keyIndices = this._keyIndices; - key = String(key); - if (!_.has(keyIndices, key)) { - var arr = this._arr; - var index = arr.length; - keyIndices[key] = index; - arr.push({key: key, priority: priority}); - this._decrease(index); - return true; - } - return false; -}; - -/** - * Removes and returns the smallest key in the queue. Takes `O(log n)` time. - */ -PriorityQueue.prototype.removeMin = function() { - this._swap(0, this._arr.length - 1); - var min = this._arr.pop(); - delete this._keyIndices[min.key]; - this._heapify(0); - return min.key; -}; - -/** - * Decreases the priority for **key** to **priority**. If the new priority is - * greater than the previous priority, this function will throw an Error. - * - * @param {Object} key the key for which to raise priority - * @param {Number} priority the new priority for the key - */ -PriorityQueue.prototype.decrease = function(key, priority) { - var index = this._keyIndices[key]; - if (priority > this._arr[index].priority) { - throw new Error("New priority is greater than current priority. " + - "Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority); - } - this._arr[index].priority = priority; - this._decrease(index); -}; - -PriorityQueue.prototype._heapify = function(i) { - var arr = this._arr; - var l = 2 * i, - r = l + 1, - largest = i; - if (l < arr.length) { - largest = arr[l].priority < arr[largest].priority ? l : largest; - if (r < arr.length) { - largest = arr[r].priority < arr[largest].priority ? r : largest; - } - if (largest !== i) { - this._swap(i, largest); - this._heapify(largest); - } - } -}; - -PriorityQueue.prototype._decrease = function(index) { - var arr = this._arr; - var priority = arr[index].priority; - var parent; - while (index !== 0) { - parent = index >> 1; - if (arr[parent].priority < priority) { - break; - } - this._swap(index, parent); - index = parent; - } -}; - -PriorityQueue.prototype._swap = function(i, j) { - var arr = this._arr; - var keyIndices = this._keyIndices; - var origArrI = arr[i]; - var origArrJ = arr[j]; - arr[i] = origArrJ; - arr[j] = origArrI; - keyIndices[origArrJ.key] = i; - keyIndices[origArrI.key] = j; -}; - -},{"../lodash":49}],46:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"); - -module.exports = Graph; - -var DEFAULT_EDGE_NAME = "\x00", - GRAPH_NODE = "\x00", - EDGE_KEY_DELIM = "\x01"; - -// Implementation notes: -// -// * Node id query functions should return string ids for the nodes -// * Edge id query functions should return an "edgeObj", edge object, that is -// composed of enough information to uniquely identify an edge: {v, w, name}. -// * Internally we use an "edgeId", a stringified form of the edgeObj, to -// reference edges. This is because we need a performant way to look these -// edges up and, object properties, which have string keys, are the closest -// we're going to get to a performant hashtable in JavaScript. - -function Graph(opts) { - this._isDirected = _.has(opts, "directed") ? opts.directed : true; - this._isMultigraph = _.has(opts, "multigraph") ? opts.multigraph : false; - this._isCompound = _.has(opts, "compound") ? opts.compound : false; - - // Label for the graph itself - this._label = undefined; - - // Defaults to be set when creating a new node - this._defaultNodeLabelFn = _.constant(undefined); - - // Defaults to be set when creating a new edge - this._defaultEdgeLabelFn = _.constant(undefined); - - // v -> label - this._nodes = {}; - - if (this._isCompound) { - // v -> parent - this._parent = {}; - - // v -> children - this._children = {}; - this._children[GRAPH_NODE] = {}; - } - - // v -> edgeObj - this._in = {}; - - // u -> v -> Number - this._preds = {}; - - // v -> edgeObj - this._out = {}; - - // v -> w -> Number - this._sucs = {}; - - // e -> edgeObj - this._edgeObjs = {}; - - // e -> label - this._edgeLabels = {}; -} - -/* Number of nodes in the graph. Should only be changed by the implementation. */ -Graph.prototype._nodeCount = 0; - -/* Number of edges in the graph. Should only be changed by the implementation. */ -Graph.prototype._edgeCount = 0; - - -/* === Graph functions ========= */ - -Graph.prototype.isDirected = function() { - return this._isDirected; -}; - -Graph.prototype.isMultigraph = function() { - return this._isMultigraph; -}; - -Graph.prototype.isCompound = function() { - return this._isCompound; -}; - -Graph.prototype.setGraph = function(label) { - this._label = label; - return this; -}; - -Graph.prototype.graph = function() { - return this._label; -}; - - -/* === Node functions ========== */ - -Graph.prototype.setDefaultNodeLabel = function(newDefault) { - if (!_.isFunction(newDefault)) { - newDefault = _.constant(newDefault); - } - this._defaultNodeLabelFn = newDefault; - return this; -}; - -Graph.prototype.nodeCount = function() { - return this._nodeCount; -}; - -Graph.prototype.nodes = function() { - return _.keys(this._nodes); -}; - -Graph.prototype.sources = function() { - return _.filter(this.nodes(), function(v) { - return _.isEmpty(this._in[v]); - }, this); -}; - -Graph.prototype.sinks = function() { - return _.filter(this.nodes(), function(v) { - return _.isEmpty(this._out[v]); - }, this); -}; - -Graph.prototype.setNodes = function(vs, value) { - var args = arguments; - _.each(vs, function(v) { - if (args.length > 1) { - this.setNode(v, value); - } else { - this.setNode(v); - } - }, this); - return this; -}; - -Graph.prototype.setNode = function(v, value) { - if (_.has(this._nodes, v)) { - if (arguments.length > 1) { - this._nodes[v] = value; - } - return this; - } - - this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v); - if (this._isCompound) { - this._parent[v] = GRAPH_NODE; - this._children[v] = {}; - this._children[GRAPH_NODE][v] = true; - } - this._in[v] = {}; - this._preds[v] = {}; - this._out[v] = {}; - this._sucs[v] = {}; - ++this._nodeCount; - return this; -}; - -Graph.prototype.node = function(v) { - return this._nodes[v]; -}; - -Graph.prototype.hasNode = function(v) { - return _.has(this._nodes, v); -}; - -Graph.prototype.removeNode = function(v) { - var self = this; - if (_.has(this._nodes, v)) { - var removeEdge = function(e) { self.removeEdge(self._edgeObjs[e]); }; - delete this._nodes[v]; - if (this._isCompound) { - this._removeFromParentsChildList(v); - delete this._parent[v]; - _.each(this.children(v), function(child) { - this.setParent(child); - }, this); - delete this._children[v]; - } - _.each(_.keys(this._in[v]), removeEdge); - delete this._in[v]; - delete this._preds[v]; - _.each(_.keys(this._out[v]), removeEdge); - delete this._out[v]; - delete this._sucs[v]; - --this._nodeCount; - } - return this; -}; - -Graph.prototype.setParent = function(v, parent) { - if (!this._isCompound) { - throw new Error("Cannot set parent in a non-compound graph"); - } - - if (_.isUndefined(parent)) { - parent = GRAPH_NODE; - } else { - // Coerce parent to string - parent += ""; - for (var ancestor = parent; - !_.isUndefined(ancestor); - ancestor = this.parent(ancestor)) { - if (ancestor === v) { - throw new Error("Setting " + parent+ " as parent of " + v + - " would create create a cycle"); - } - } - - this.setNode(parent); - } - - this.setNode(v); - this._removeFromParentsChildList(v); - this._parent[v] = parent; - this._children[parent][v] = true; - return this; -}; - -Graph.prototype._removeFromParentsChildList = function(v) { - delete this._children[this._parent[v]][v]; -}; - -Graph.prototype.parent = function(v) { - if (this._isCompound) { - var parent = this._parent[v]; - if (parent !== GRAPH_NODE) { - return parent; - } - } -}; - -Graph.prototype.children = function(v) { - if (_.isUndefined(v)) { - v = GRAPH_NODE; - } - - if (this._isCompound) { - var children = this._children[v]; - if (children) { - return _.keys(children); - } - } else if (v === GRAPH_NODE) { - return this.nodes(); - } else if (this.hasNode(v)) { - return []; - } -}; - -Graph.prototype.predecessors = function(v) { - var predsV = this._preds[v]; - if (predsV) { - return _.keys(predsV); - } -}; - -Graph.prototype.successors = function(v) { - var sucsV = this._sucs[v]; - if (sucsV) { - return _.keys(sucsV); - } -}; - -Graph.prototype.neighbors = function(v) { - var preds = this.predecessors(v); - if (preds) { - return _.union(preds, this.successors(v)); - } -}; - -/* === Edge functions ========== */ - -Graph.prototype.setDefaultEdgeLabel = function(newDefault) { - if (!_.isFunction(newDefault)) { - newDefault = _.constant(newDefault); - } - this._defaultEdgeLabelFn = newDefault; - return this; -}; - -Graph.prototype.edgeCount = function() { - return this._edgeCount; -}; - -Graph.prototype.edges = function() { - return _.values(this._edgeObjs); -}; - -Graph.prototype.setPath = function(vs, value) { - var self = this, - args = arguments; - _.reduce(vs, function(v, w) { - if (args.length > 1) { - self.setEdge(v, w, value); - } else { - self.setEdge(v, w); - } - return w; - }); - return this; -}; - -/* - * setEdge(v, w, [value, [name]]) - * setEdge({ v, w, [name] }, [value]) - */ -Graph.prototype.setEdge = function() { - var v, w, name, value, - valueSpecified = false; - - if (_.isPlainObject(arguments[0])) { - v = arguments[0].v; - w = arguments[0].w; - name = arguments[0].name; - if (arguments.length === 2) { - value = arguments[1]; - valueSpecified = true; - } - } else { - v = arguments[0]; - w = arguments[1]; - name = arguments[3]; - if (arguments.length > 2) { - value = arguments[2]; - valueSpecified = true; - } - } - - v = "" + v; - w = "" + w; - if (!_.isUndefined(name)) { - name = "" + name; - } - - var e = edgeArgsToId(this._isDirected, v, w, name); - if (_.has(this._edgeLabels, e)) { - if (valueSpecified) { - this._edgeLabels[e] = value; - } - return this; - } - - if (!_.isUndefined(name) && !this._isMultigraph) { - throw new Error("Cannot set a named edge when isMultigraph = false"); - } - - // It didn't exist, so we need to create it. - // First ensure the nodes exist. - this.setNode(v); - this.setNode(w); - - this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name); - - var edgeObj = edgeArgsToObj(this._isDirected, v, w, name); - // Ensure we add undirected edges in a consistent way. - v = edgeObj.v; - w = edgeObj.w; - - Object.freeze(edgeObj); - this._edgeObjs[e] = edgeObj; - incrementOrInitEntry(this._preds[w], v); - incrementOrInitEntry(this._sucs[v], w); - this._in[w][e] = edgeObj; - this._out[v][e] = edgeObj; - this._edgeCount++; - return this; -}; - -Graph.prototype.edge = function(v, w, name) { - var e = (arguments.length === 1 - ? edgeObjToId(this._isDirected, arguments[0]) - : edgeArgsToId(this._isDirected, v, w, name)); - return this._edgeLabels[e]; -}; - -Graph.prototype.hasEdge = function(v, w, name) { - var e = (arguments.length === 1 - ? edgeObjToId(this._isDirected, arguments[0]) - : edgeArgsToId(this._isDirected, v, w, name)); - return _.has(this._edgeLabels, e); -}; - -Graph.prototype.removeEdge = function(v, w, name) { - var e = (arguments.length === 1 - ? edgeObjToId(this._isDirected, arguments[0]) - : edgeArgsToId(this._isDirected, v, w, name)), - edge = this._edgeObjs[e]; - if (edge) { - v = edge.v; - w = edge.w; - delete this._edgeLabels[e]; - delete this._edgeObjs[e]; - decrementOrRemoveEntry(this._preds[w], v); - decrementOrRemoveEntry(this._sucs[v], w); - delete this._in[w][e]; - delete this._out[v][e]; - this._edgeCount--; - } - return this; -}; - -Graph.prototype.inEdges = function(v, u) { - var inV = this._in[v]; - if (inV) { - var edges = _.values(inV); - if (!u) { - return edges; - } - return _.filter(edges, function(edge) { return edge.v === u; }); - } -}; - -Graph.prototype.outEdges = function(v, w) { - var outV = this._out[v]; - if (outV) { - var edges = _.values(outV); - if (!w) { - return edges; - } - return _.filter(edges, function(edge) { return edge.w === w; }); - } -}; - -Graph.prototype.nodeEdges = function(v, w) { - var inEdges = this.inEdges(v, w); - if (inEdges) { - return inEdges.concat(this.outEdges(v, w)); - } -}; - -function incrementOrInitEntry(map, k) { - if (_.has(map, k)) { - map[k]++; - } else { - map[k] = 1; - } -} - -function decrementOrRemoveEntry(map, k) { - if (!--map[k]) { delete map[k]; } -} - -function edgeArgsToId(isDirected, v, w, name) { - if (!isDirected && v > w) { - var tmp = v; - v = w; - w = tmp; - } - return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM + - (_.isUndefined(name) ? DEFAULT_EDGE_NAME : name); -} - -function edgeArgsToObj(isDirected, v, w, name) { - if (!isDirected && v > w) { - var tmp = v; - v = w; - w = tmp; - } - var edgeObj = { v: v, w: w }; - if (name) { - edgeObj.name = name; - } - return edgeObj; -} - -function edgeObjToId(isDirected, edgeObj) { - return edgeArgsToId(isDirected, edgeObj.v, edgeObj.w, edgeObj.name); -} - -},{"./lodash":49}],47:[function(require,module,exports){ -// Includes only the "core" of graphlib -module.exports = { - Graph: require("./graph"), - version: require("./version") -}; - -},{"./graph":46,"./version":50}],48:[function(require,module,exports){ -var _ = require("./lodash"), - Graph = require("./graph"); - -module.exports = { - write: write, - read: read -}; - -function write(g) { - var json = { - options: { - directed: g.isDirected(), - multigraph: g.isMultigraph(), - compound: g.isCompound() - }, - nodes: writeNodes(g), - edges: writeEdges(g) - }; - if (!_.isUndefined(g.graph())) { - json.value = _.clone(g.graph()); - } - return json; -} - -function writeNodes(g) { - return _.map(g.nodes(), function(v) { - var nodeValue = g.node(v), - parent = g.parent(v), - node = { v: v }; - if (!_.isUndefined(nodeValue)) { - node.value = nodeValue; - } - if (!_.isUndefined(parent)) { - node.parent = parent; - } - return node; - }); -} - -function writeEdges(g) { - return _.map(g.edges(), function(e) { - var edgeValue = g.edge(e), - edge = { v: e.v, w: e.w }; - if (!_.isUndefined(e.name)) { - edge.name = e.name; - } - if (!_.isUndefined(edgeValue)) { - edge.value = edgeValue; - } - return edge; - }); -} - -function read(json) { - var g = new Graph(json.options).setGraph(json.value); - _.each(json.nodes, function(entry) { - g.setNode(entry.v, entry.value); - if (entry.parent) { - g.setParent(entry.v, entry.parent); - } - }); - _.each(json.edges, function(entry) { - g.setEdge({ v: entry.v, w: entry.w, name: entry.name }, entry.value); - }); - return g; -} - -},{"./graph":46,"./lodash":49}],49:[function(require,module,exports){ -module.exports=require(10) -},{"/Users/cpettitt/projects/dagre/lib/lodash.js":10,"lodash":51}],50:[function(require,module,exports){ -module.exports = '1.0.5'; - -},{}],51:[function(require,module,exports){ -(function (global){ -/** - * @license - * lodash 3.10.0 (Custom Build) - * Build: `lodash modern -d -o ./index.js` - * Copyright 2012-2015 The Dojo Foundation - * Based on Underscore.js 1.8.3 - * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ -;(function() { - - /** Used as a safe reference for `undefined` in pre-ES5 environments. */ - var undefined; - - /** Used as the semantic version number. */ - var VERSION = '3.10.0'; - - /** Used to compose bitmasks for wrapper metadata. */ - var BIND_FLAG = 1, - BIND_KEY_FLAG = 2, - CURRY_BOUND_FLAG = 4, - CURRY_FLAG = 8, - CURRY_RIGHT_FLAG = 16, - PARTIAL_FLAG = 32, - PARTIAL_RIGHT_FLAG = 64, - ARY_FLAG = 128, - REARG_FLAG = 256; - - /** Used as default options for `_.trunc`. */ - var DEFAULT_TRUNC_LENGTH = 30, - DEFAULT_TRUNC_OMISSION = '...'; - - /** Used to detect when a function becomes hot. */ - var HOT_COUNT = 150, - HOT_SPAN = 16; - - /** Used as the size to enable large array optimizations. */ - var LARGE_ARRAY_SIZE = 200; - - /** Used to indicate the type of lazy iteratees. */ - var LAZY_FILTER_FLAG = 1, - LAZY_MAP_FLAG = 2; - - /** Used as the `TypeError` message for "Functions" methods. */ - var FUNC_ERROR_TEXT = 'Expected a function'; - - /** Used as the internal argument placeholder. */ - var PLACEHOLDER = '__lodash_placeholder__'; - - /** `Object#toString` result references. */ - var argsTag = '[object Arguments]', - arrayTag = '[object Array]', - boolTag = '[object Boolean]', - dateTag = '[object Date]', - errorTag = '[object Error]', - funcTag = '[object Function]', - mapTag = '[object Map]', - numberTag = '[object Number]', - objectTag = '[object Object]', - regexpTag = '[object RegExp]', - setTag = '[object Set]', - stringTag = '[object String]', - weakMapTag = '[object WeakMap]'; - - var arrayBufferTag = '[object ArrayBuffer]', - float32Tag = '[object Float32Array]', - float64Tag = '[object Float64Array]', - int8Tag = '[object Int8Array]', - int16Tag = '[object Int16Array]', - int32Tag = '[object Int32Array]', - uint8Tag = '[object Uint8Array]', - uint8ClampedTag = '[object Uint8ClampedArray]', - uint16Tag = '[object Uint16Array]', - uint32Tag = '[object Uint32Array]'; - - /** Used to match empty string literals in compiled template source. */ - var reEmptyStringLeading = /\b__p \+= '';/g, - reEmptyStringMiddle = /\b(__p \+=) '' \+/g, - reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; - - /** Used to match HTML entities and HTML characters. */ - var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g, - reUnescapedHtml = /[&<>"'`]/g, - reHasEscapedHtml = RegExp(reEscapedHtml.source), - reHasUnescapedHtml = RegExp(reUnescapedHtml.source); - - /** Used to match template delimiters. */ - var reEscape = /<%-([\s\S]+?)%>/g, - reEvaluate = /<%([\s\S]+?)%>/g, - reInterpolate = /<%=([\s\S]+?)%>/g; - - /** Used to match property names within property paths. */ - var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/, - reIsPlainProp = /^\w*$/, - rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; - - /** - * Used to match `RegExp` [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns) - * and those outlined by [`EscapeRegExpPattern`](http://ecma-international.org/ecma-262/6.0/#sec-escaperegexppattern). - */ - var reRegExpChars = /^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g, - reHasRegExpChars = RegExp(reRegExpChars.source); - - /** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */ - var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g; - - /** Used to match backslashes in property paths. */ - var reEscapeChar = /\\(\\)?/g; - - /** Used to match [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components). */ - var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - - /** Used to match `RegExp` flags from their coerced string values. */ - var reFlags = /\w*$/; - - /** Used to detect hexadecimal string values. */ - var reHasHexPrefix = /^0[xX]/; - - /** Used to detect host constructors (Safari > 5). */ - var reIsHostCtor = /^\[object .+?Constructor\]$/; - - /** Used to detect unsigned integer values. */ - var reIsUint = /^\d+$/; - - /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ - var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; - - /** Used to ensure capturing order of template delimiters. */ - var reNoMatch = /($^)/; - - /** Used to match unescaped characters in compiled string literals. */ - var reUnescapedString = /['\n\r\u2028\u2029\\]/g; - - /** Used to match words to create compound words. */ - var reWords = (function() { - var upper = '[A-Z\\xc0-\\xd6\\xd8-\\xde]', - lower = '[a-z\\xdf-\\xf6\\xf8-\\xff]+'; - - return RegExp(upper + '+(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g'); - }()); - - /** Used to assign default `context` object properties. */ - var contextProps = [ - 'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array', - 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number', - 'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'isFinite', - 'parseFloat', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array', - 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap' - ]; - - /** Used to make template sourceURLs easier to identify. */ - var templateCounter = -1; - - /** Used to identify `toStringTag` values of typed arrays. */ - var typedArrayTags = {}; - typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = - typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = - typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = - typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = - typedArrayTags[uint32Tag] = true; - typedArrayTags[argsTag] = typedArrayTags[arrayTag] = - typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = - typedArrayTags[dateTag] = typedArrayTags[errorTag] = - typedArrayTags[funcTag] = typedArrayTags[mapTag] = - typedArrayTags[numberTag] = typedArrayTags[objectTag] = - typedArrayTags[regexpTag] = typedArrayTags[setTag] = - typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; - - /** Used to identify `toStringTag` values supported by `_.clone`. */ - var cloneableTags = {}; - cloneableTags[argsTag] = cloneableTags[arrayTag] = - cloneableTags[arrayBufferTag] = cloneableTags[boolTag] = - cloneableTags[dateTag] = cloneableTags[float32Tag] = - cloneableTags[float64Tag] = cloneableTags[int8Tag] = - cloneableTags[int16Tag] = cloneableTags[int32Tag] = - cloneableTags[numberTag] = cloneableTags[objectTag] = - cloneableTags[regexpTag] = cloneableTags[stringTag] = - cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = - cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; - cloneableTags[errorTag] = cloneableTags[funcTag] = - cloneableTags[mapTag] = cloneableTags[setTag] = - cloneableTags[weakMapTag] = false; - - /** Used to map latin-1 supplementary letters to basic latin letters. */ - var deburredLetters = { - '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', - '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', - '\xc7': 'C', '\xe7': 'c', - '\xd0': 'D', '\xf0': 'd', - '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', - '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', - '\xcC': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', - '\xeC': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', - '\xd1': 'N', '\xf1': 'n', - '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', - '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', - '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', - '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', - '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', - '\xc6': 'Ae', '\xe6': 'ae', - '\xde': 'Th', '\xfe': 'th', - '\xdf': 'ss' - }; - - /** Used to map characters to HTML entities. */ - var htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '`': '`' - }; - - /** Used to map HTML entities to characters. */ - var htmlUnescapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - ''': "'", - '`': '`' - }; - - /** Used to determine if values are of the language type `Object`. */ - var objectTypes = { - 'function': true, - 'object': true - }; - - /** Used to escape characters for inclusion in compiled regexes. */ - var regexpEscapes = { - '0': 'x30', '1': 'x31', '2': 'x32', '3': 'x33', '4': 'x34', - '5': 'x35', '6': 'x36', '7': 'x37', '8': 'x38', '9': 'x39', - 'A': 'x41', 'B': 'x42', 'C': 'x43', 'D': 'x44', 'E': 'x45', 'F': 'x46', - 'a': 'x61', 'b': 'x62', 'c': 'x63', 'd': 'x64', 'e': 'x65', 'f': 'x66', - 'n': 'x6e', 'r': 'x72', 't': 'x74', 'u': 'x75', 'v': 'x76', 'x': 'x78' - }; - - /** Used to escape characters for inclusion in compiled string literals. */ - var stringEscapes = { - '\\': '\\', - "'": "'", - '\n': 'n', - '\r': 'r', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - /** Detect free variable `exports`. */ - var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; - - /** Detect free variable `module`. */ - var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; - - /** Detect free variable `global` from Node.js. */ - var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global; - - /** Detect free variable `self`. */ - var freeSelf = objectTypes[typeof self] && self && self.Object && self; - - /** Detect free variable `window`. */ - var freeWindow = objectTypes[typeof window] && window && window.Object && window; - - /** Detect the popular CommonJS extension `module.exports`. */ - var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; - - /** - * Used as a reference to the global object. - * - * The `this` value is used if it's the global object to avoid Greasemonkey's - * restricted `window` object, otherwise the `window` object is used. - */ - var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this; - - /*--------------------------------------------------------------------------*/ - - /** - * The base implementation of `compareAscending` which compares values and - * sorts them in ascending order without guaranteeing a stable sort. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {number} Returns the sort order indicator for `value`. - */ - function baseCompareAscending(value, other) { - if (value !== other) { - var valIsNull = value === null, - valIsUndef = value === undefined, - valIsReflexive = value === value; - - var othIsNull = other === null, - othIsUndef = other === undefined, - othIsReflexive = other === other; - - if ((value > other && !othIsNull) || !valIsReflexive || - (valIsNull && !othIsUndef && othIsReflexive) || - (valIsUndef && othIsReflexive)) { - return 1; - } - if ((value < other && !valIsNull) || !othIsReflexive || - (othIsNull && !valIsUndef && valIsReflexive) || - (othIsUndef && valIsReflexive)) { - return -1; - } - } - return 0; - } - - /** - * The base implementation of `_.findIndex` and `_.findLastIndex` without - * support for callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to search. - * @param {Function} predicate The function invoked per iteration. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function baseFindIndex(array, predicate, fromRight) { - var length = array.length, - index = fromRight ? length : -1; - - while ((fromRight ? index-- : ++index < length)) { - if (predicate(array[index], index, array)) { - return index; - } - } - return -1; - } - - /** - * The base implementation of `_.indexOf` without support for binary searches. - * - * @private - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {number} fromIndex The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function baseIndexOf(array, value, fromIndex) { - if (value !== value) { - return indexOfNaN(array, fromIndex); - } - var index = fromIndex - 1, - length = array.length; - - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; - } - - /** - * The base implementation of `_.isFunction` without support for environments - * with incorrect `typeof` results. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - */ - function baseIsFunction(value) { - // Avoid a Chakra JIT bug in compatibility modes of IE 11. - // See https://github.com/jashkenas/underscore/issues/1621 for more details. - return typeof value == 'function' || false; - } - - /** - * Converts `value` to a string if it's not one. An empty string is returned - * for `null` or `undefined` values. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ - function baseToString(value) { - return value == null ? '' : (value + ''); - } - - /** - * Used by `_.trim` and `_.trimLeft` to get the index of the first character - * of `string` that is not found in `chars`. - * - * @private - * @param {string} string The string to inspect. - * @param {string} chars The characters to find. - * @returns {number} Returns the index of the first character not found in `chars`. - */ - function charsLeftIndex(string, chars) { - var index = -1, - length = string.length; - - while (++index < length && chars.indexOf(string.charAt(index)) > -1) {} - return index; - } - - /** - * Used by `_.trim` and `_.trimRight` to get the index of the last character - * of `string` that is not found in `chars`. - * - * @private - * @param {string} string The string to inspect. - * @param {string} chars The characters to find. - * @returns {number} Returns the index of the last character not found in `chars`. - */ - function charsRightIndex(string, chars) { - var index = string.length; - - while (index-- && chars.indexOf(string.charAt(index)) > -1) {} - return index; - } - - /** - * Used by `_.sortBy` to compare transformed elements of a collection and stable - * sort them in ascending order. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @returns {number} Returns the sort order indicator for `object`. - */ - function compareAscending(object, other) { - return baseCompareAscending(object.criteria, other.criteria) || (object.index - other.index); - } - - /** - * Used by `_.sortByOrder` to compare multiple properties of a value to another - * and stable sort them. - * - * If `orders` is unspecified, all valuess are sorted in ascending order. Otherwise, - * a value is sorted in ascending order if its corresponding order is "asc", and - * descending if "desc". - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {boolean[]} orders The order to sort by for each property. - * @returns {number} Returns the sort order indicator for `object`. - */ - function compareMultiple(object, other, orders) { - var index = -1, - objCriteria = object.criteria, - othCriteria = other.criteria, - length = objCriteria.length, - ordersLength = orders.length; - - while (++index < length) { - var result = baseCompareAscending(objCriteria[index], othCriteria[index]); - if (result) { - if (index >= ordersLength) { - return result; - } - var order = orders[index]; - return result * ((order === 'asc' || order === true) ? 1 : -1); - } - } - // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications - // that causes it, under certain circumstances, to provide the same value for - // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 - // for more details. - // - // This also ensures a stable sort in V8 and other engines. - // See https://code.google.com/p/v8/issues/detail?id=90 for more details. - return object.index - other.index; - } - - /** - * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters. - * - * @private - * @param {string} letter The matched letter to deburr. - * @returns {string} Returns the deburred letter. - */ - function deburrLetter(letter) { - return deburredLetters[letter]; - } - - /** - * Used by `_.escape` to convert characters to HTML entities. - * - * @private - * @param {string} chr The matched character to escape. - * @returns {string} Returns the escaped character. - */ - function escapeHtmlChar(chr) { - return htmlEscapes[chr]; - } - - /** - * Used by `_.escapeRegExp` to escape characters for inclusion in compiled regexes. - * - * @private - * @param {string} chr The matched character to escape. - * @param {string} leadingChar The capture group for a leading character. - * @param {string} whitespaceChar The capture group for a whitespace character. - * @returns {string} Returns the escaped character. - */ - function escapeRegExpChar(chr, leadingChar, whitespaceChar) { - if (leadingChar) { - chr = regexpEscapes[chr]; - } else if (whitespaceChar) { - chr = stringEscapes[chr]; - } - return '\\' + chr; - } - - /** - * Used by `_.template` to escape characters for inclusion in compiled string literals. - * - * @private - * @param {string} chr The matched character to escape. - * @returns {string} Returns the escaped character. - */ - function escapeStringChar(chr) { - return '\\' + stringEscapes[chr]; - } - - /** - * Gets the index at which the first occurrence of `NaN` is found in `array`. - * - * @private - * @param {Array} array The array to search. - * @param {number} fromIndex The index to search from. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {number} Returns the index of the matched `NaN`, else `-1`. - */ - function indexOfNaN(array, fromIndex, fromRight) { - var length = array.length, - index = fromIndex + (fromRight ? 0 : -1); - - while ((fromRight ? index-- : ++index < length)) { - var other = array[index]; - if (other !== other) { - return index; - } - } - return -1; - } - - /** - * Checks if `value` is object-like. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - */ - function isObjectLike(value) { - return !!value && typeof value == 'object'; - } - - /** - * Used by `trimmedLeftIndex` and `trimmedRightIndex` to determine if a - * character code is whitespace. - * - * @private - * @param {number} charCode The character code to inspect. - * @returns {boolean} Returns `true` if `charCode` is whitespace, else `false`. - */ - function isSpace(charCode) { - return ((charCode <= 160 && (charCode >= 9 && charCode <= 13) || charCode == 32 || charCode == 160) || charCode == 5760 || charCode == 6158 || - (charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279))); - } - - /** - * Replaces all `placeholder` elements in `array` with an internal placeholder - * and returns an array of their indexes. - * - * @private - * @param {Array} array The array to modify. - * @param {*} placeholder The placeholder to replace. - * @returns {Array} Returns the new array of placeholder indexes. - */ - function replaceHolders(array, placeholder) { - var index = -1, - length = array.length, - resIndex = -1, - result = []; - - while (++index < length) { - if (array[index] === placeholder) { - array[index] = PLACEHOLDER; - result[++resIndex] = index; - } - } - return result; - } - - /** - * An implementation of `_.uniq` optimized for sorted arrays without support - * for callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The function invoked per iteration. - * @returns {Array} Returns the new duplicate-value-free array. - */ - function sortedUniq(array, iteratee) { - var seen, - index = -1, - length = array.length, - resIndex = -1, - result = []; - - while (++index < length) { - var value = array[index], - computed = iteratee ? iteratee(value, index, array) : value; - - if (!index || seen !== computed) { - seen = computed; - result[++resIndex] = value; - } - } - return result; - } - - /** - * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace - * character of `string`. - * - * @private - * @param {string} string The string to inspect. - * @returns {number} Returns the index of the first non-whitespace character. - */ - function trimmedLeftIndex(string) { - var index = -1, - length = string.length; - - while (++index < length && isSpace(string.charCodeAt(index))) {} - return index; - } - - /** - * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace - * character of `string`. - * - * @private - * @param {string} string The string to inspect. - * @returns {number} Returns the index of the last non-whitespace character. - */ - function trimmedRightIndex(string) { - var index = string.length; - - while (index-- && isSpace(string.charCodeAt(index))) {} - return index; - } - - /** - * Used by `_.unescape` to convert HTML entities to characters. - * - * @private - * @param {string} chr The matched character to unescape. - * @returns {string} Returns the unescaped character. - */ - function unescapeHtmlChar(chr) { - return htmlUnescapes[chr]; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Create a new pristine `lodash` function using the given `context` object. - * - * @static - * @memberOf _ - * @category Utility - * @param {Object} [context=root] The context object. - * @returns {Function} Returns a new `lodash` function. - * @example - * - * _.mixin({ 'foo': _.constant('foo') }); - * - * var lodash = _.runInContext(); - * lodash.mixin({ 'bar': lodash.constant('bar') }); - * - * _.isFunction(_.foo); - * // => true - * _.isFunction(_.bar); - * // => false - * - * lodash.isFunction(lodash.foo); - * // => false - * lodash.isFunction(lodash.bar); - * // => true - * - * // using `context` to mock `Date#getTime` use in `_.now` - * var mock = _.runInContext({ - * 'Date': function() { - * return { 'getTime': getTimeMock }; - * } - * }); - * - * // or creating a suped-up `defer` in Node.js - * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; - */ - function runInContext(context) { - // Avoid issues with some ES3 environments that attempt to use values, named - // after built-in constructors like `Object`, for the creation of literals. - // ES5 clears this up by stating that literals must use built-in constructors. - // See https://es5.github.io/#x11.1.5 for more details. - context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root; - - /** Native constructor references. */ - var Array = context.Array, - Date = context.Date, - Error = context.Error, - Function = context.Function, - Math = context.Math, - Number = context.Number, - Object = context.Object, - RegExp = context.RegExp, - String = context.String, - TypeError = context.TypeError; - - /** Used for native method references. */ - var arrayProto = Array.prototype, - objectProto = Object.prototype, - stringProto = String.prototype; - - /** Used to resolve the decompiled source of functions. */ - var fnToString = Function.prototype.toString; - - /** Used to check objects for own properties. */ - var hasOwnProperty = objectProto.hasOwnProperty; - - /** Used to generate unique IDs. */ - var idCounter = 0; - - /** - * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) - * of values. - */ - var objToString = objectProto.toString; - - /** Used to restore the original `_` reference in `_.noConflict`. */ - var oldDash = root._; - - /** Used to detect if a method is native. */ - var reIsNative = RegExp('^' + - fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&') - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' - ); - - /** Native method references. */ - var ArrayBuffer = context.ArrayBuffer, - clearTimeout = context.clearTimeout, - parseFloat = context.parseFloat, - pow = Math.pow, - propertyIsEnumerable = objectProto.propertyIsEnumerable, - Set = getNative(context, 'Set'), - setTimeout = context.setTimeout, - splice = arrayProto.splice, - Uint8Array = context.Uint8Array, - WeakMap = getNative(context, 'WeakMap'); - - /* Native method references for those with the same name as other `lodash` methods. */ - var nativeCeil = Math.ceil, - nativeCreate = getNative(Object, 'create'), - nativeFloor = Math.floor, - nativeIsArray = getNative(Array, 'isArray'), - nativeIsFinite = context.isFinite, - nativeKeys = getNative(Object, 'keys'), - nativeMax = Math.max, - nativeMin = Math.min, - nativeNow = getNative(Date, 'now'), - nativeParseInt = context.parseInt, - nativeRandom = Math.random; - - /** Used as references for `-Infinity` and `Infinity`. */ - var NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY, - POSITIVE_INFINITY = Number.POSITIVE_INFINITY; - - /** Used as references for the maximum length and index of an array. */ - var MAX_ARRAY_LENGTH = 4294967295, - MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, - HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; - - /** - * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer) - * of an array-like value. - */ - var MAX_SAFE_INTEGER = 9007199254740991; - - /** Used to store function metadata. */ - var metaMap = WeakMap && new WeakMap; - - /** Used to lookup unminified function names. */ - var realNames = {}; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` object which wraps `value` to enable implicit chaining. - * Methods that operate on and return arrays, collections, and functions can - * be chained together. Methods that retrieve a single value or may return a - * primitive value will automatically end the chain returning the unwrapped - * value. Explicit chaining may be enabled using `_.chain`. The execution of - * chained methods is lazy, that is, execution is deferred until `_#value` - * is implicitly or explicitly called. - * - * Lazy evaluation allows several methods to support shortcut fusion. Shortcut - * fusion is an optimization strategy which merge iteratee calls; this can help - * to avoid the creation of intermediate data structures and greatly reduce the - * number of iteratee executions. - * - * Chaining is supported in custom builds as long as the `_#value` method is - * directly or indirectly included in the build. - * - * In addition to lodash methods, wrappers have `Array` and `String` methods. - * - * The wrapper `Array` methods are: - * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, - * `splice`, and `unshift` - * - * The wrapper `String` methods are: - * `replace` and `split` - * - * The wrapper methods that support shortcut fusion are: - * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, - * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, - * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`, - * and `where` - * - * The chainable wrapper methods are: - * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`, - * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`, - * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defaultsDeep`, - * `defer`, `delay`, `difference`, `drop`, `dropRight`, `dropRightWhile`, - * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, - * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, - * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, - * `invoke`, `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, - * `matchesProperty`, `memoize`, `merge`, `method`, `methodOf`, `mixin`, - * `modArgs`, `negate`, `omit`, `once`, `pairs`, `partial`, `partialRight`, - * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`, - * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `restParam`, - * `reverse`, `set`, `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, - * `sortByOrder`, `splice`, `spread`, `take`, `takeRight`, `takeRightWhile`, - * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`, - * `transform`, `union`, `uniq`, `unshift`, `unzip`, `unzipWith`, `values`, - * `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, `zipObject`, `zipWith` - * - * The wrapper methods that are **not** chainable by default are: - * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clone`, `cloneDeep`, - * `deburr`, `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, - * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, - * `floor`, `get`, `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, - * `inRange`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, - * `isEmpty`, `isEqual`, `isError`, `isFinite` `isFunction`, `isMatch`, - * `isNative`, `isNaN`, `isNull`, `isNumber`, `isObject`, `isPlainObject`, - * `isRegExp`, `isString`, `isUndefined`, `isTypedArray`, `join`, `kebabCase`, - * `last`, `lastIndexOf`, `lt`, `lte`, `max`, `min`, `noConflict`, `noop`, - * `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, `random`, `reduce`, - * `reduceRight`, `repeat`, `result`, `round`, `runInContext`, `shift`, `size`, - * `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, `startCase`, - * `startsWith`, `sum`, `template`, `trim`, `trimLeft`, `trimRight`, `trunc`, - * `unescape`, `uniqueId`, `value`, and `words` - * - * The wrapper method `sample` will return a wrapped value when `n` is provided, - * otherwise an unwrapped value is returned. - * - * @name _ - * @constructor - * @category Chain - * @param {*} value The value to wrap in a `lodash` instance. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var wrapped = _([1, 2, 3]); - * - * // returns an unwrapped value - * wrapped.reduce(function(total, n) { - * return total + n; - * }); - * // => 6 - * - * // returns a wrapped value - * var squares = wrapped.map(function(n) { - * return n * n; - * }); - * - * _.isArray(squares); - * // => false - * - * _.isArray(squares.value()); - * // => true - */ - function lodash(value) { - if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { - if (value instanceof LodashWrapper) { - return value; - } - if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) { - return wrapperClone(value); - } - } - return new LodashWrapper(value); - } - - /** - * The function whose prototype all chaining wrappers inherit from. - * - * @private - */ - function baseLodash() { - // No operation performed. - } - - /** - * The base constructor for creating `lodash` wrapper objects. - * - * @private - * @param {*} value The value to wrap. - * @param {boolean} [chainAll] Enable chaining for all wrapper methods. - * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value. - */ - function LodashWrapper(value, chainAll, actions) { - this.__wrapped__ = value; - this.__actions__ = actions || []; - this.__chain__ = !!chainAll; - } - - /** - * An object environment feature flags. - * - * @static - * @memberOf _ - * @type Object - */ - var support = lodash.support = {}; - - /** - * By default, the template delimiters used by lodash are like those in - * embedded Ruby (ERB). Change the following template settings to use - * alternative delimiters. - * - * @static - * @memberOf _ - * @type Object - */ - lodash.templateSettings = { - - /** - * Used to detect `data` property values to be HTML-escaped. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'escape': reEscape, - - /** - * Used to detect code to be evaluated. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'evaluate': reEvaluate, - - /** - * Used to detect `data` property values to inject. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'interpolate': reInterpolate, - - /** - * Used to reference the data object in the template text. - * - * @memberOf _.templateSettings - * @type string - */ - 'variable': '', - - /** - * Used to import variables into the compiled template. - * - * @memberOf _.templateSettings - * @type Object - */ - 'imports': { - - /** - * A reference to the `lodash` function. - * - * @memberOf _.templateSettings.imports - * @type Function - */ - '_': lodash - } - }; - - /*------------------------------------------------------------------------*/ - - /** - * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. - * - * @private - * @param {*} value The value to wrap. - */ - function LazyWrapper(value) { - this.__wrapped__ = value; - this.__actions__ = []; - this.__dir__ = 1; - this.__filtered__ = false; - this.__iteratees__ = []; - this.__takeCount__ = POSITIVE_INFINITY; - this.__views__ = []; - } - - /** - * Creates a clone of the lazy wrapper object. - * - * @private - * @name clone - * @memberOf LazyWrapper - * @returns {Object} Returns the cloned `LazyWrapper` object. - */ - function lazyClone() { - var result = new LazyWrapper(this.__wrapped__); - result.__actions__ = arrayCopy(this.__actions__); - result.__dir__ = this.__dir__; - result.__filtered__ = this.__filtered__; - result.__iteratees__ = arrayCopy(this.__iteratees__); - result.__takeCount__ = this.__takeCount__; - result.__views__ = arrayCopy(this.__views__); - return result; - } - - /** - * Reverses the direction of lazy iteration. - * - * @private - * @name reverse - * @memberOf LazyWrapper - * @returns {Object} Returns the new reversed `LazyWrapper` object. - */ - function lazyReverse() { - if (this.__filtered__) { - var result = new LazyWrapper(this); - result.__dir__ = -1; - result.__filtered__ = true; - } else { - result = this.clone(); - result.__dir__ *= -1; - } - return result; - } - - /** - * Extracts the unwrapped value from its lazy wrapper. - * - * @private - * @name value - * @memberOf LazyWrapper - * @returns {*} Returns the unwrapped value. - */ - function lazyValue() { - var array = this.__wrapped__.value(), - dir = this.__dir__, - isArr = isArray(array), - isRight = dir < 0, - arrLength = isArr ? array.length : 0, - view = getView(0, arrLength, this.__views__), - start = view.start, - end = view.end, - length = end - start, - index = isRight ? end : (start - 1), - iteratees = this.__iteratees__, - iterLength = iteratees.length, - resIndex = 0, - takeCount = nativeMin(length, this.__takeCount__); - - if (!isArr || arrLength < LARGE_ARRAY_SIZE || (arrLength == length && takeCount == length)) { - return baseWrapperValue((isRight && isArr) ? array.reverse() : array, this.__actions__); - } - var result = []; - - outer: - while (length-- && resIndex < takeCount) { - index += dir; - - var iterIndex = -1, - value = array[index]; - - while (++iterIndex < iterLength) { - var data = iteratees[iterIndex], - iteratee = data.iteratee, - type = data.type, - computed = iteratee(value); - - if (type == LAZY_MAP_FLAG) { - value = computed; - } else if (!computed) { - if (type == LAZY_FILTER_FLAG) { - continue outer; - } else { - break outer; - } - } - } - result[resIndex++] = value; - } - return result; - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates a cache object to store key/value pairs. - * - * @private - * @static - * @name Cache - * @memberOf _.memoize - */ - function MapCache() { - this.__data__ = {}; - } - - /** - * Removes `key` and its value from the cache. - * - * @private - * @name delete - * @memberOf _.memoize.Cache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed successfully, else `false`. - */ - function mapDelete(key) { - return this.has(key) && delete this.__data__[key]; - } - - /** - * Gets the cached value for `key`. - * - * @private - * @name get - * @memberOf _.memoize.Cache - * @param {string} key The key of the value to get. - * @returns {*} Returns the cached value. - */ - function mapGet(key) { - return key == '__proto__' ? undefined : this.__data__[key]; - } - - /** - * Checks if a cached value for `key` exists. - * - * @private - * @name has - * @memberOf _.memoize.Cache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function mapHas(key) { - return key != '__proto__' && hasOwnProperty.call(this.__data__, key); - } - - /** - * Sets `value` to `key` of the cache. - * - * @private - * @name set - * @memberOf _.memoize.Cache - * @param {string} key The key of the value to cache. - * @param {*} value The value to cache. - * @returns {Object} Returns the cache object. - */ - function mapSet(key, value) { - if (key != '__proto__') { - this.__data__[key] = value; - } - return this; - } - - /*------------------------------------------------------------------------*/ - - /** - * - * Creates a cache object to store unique values. - * - * @private - * @param {Array} [values] The values to cache. - */ - function SetCache(values) { - var length = values ? values.length : 0; - - this.data = { 'hash': nativeCreate(null), 'set': new Set }; - while (length--) { - this.push(values[length]); - } - } - - /** - * Checks if `value` is in `cache` mimicking the return signature of - * `_.indexOf` by returning `0` if the value is found, else `-1`. - * - * @private - * @param {Object} cache The cache to search. - * @param {*} value The value to search for. - * @returns {number} Returns `0` if `value` is found, else `-1`. - */ - function cacheIndexOf(cache, value) { - var data = cache.data, - result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value]; - - return result ? 0 : -1; - } - - /** - * Adds `value` to the cache. - * - * @private - * @name push - * @memberOf SetCache - * @param {*} value The value to cache. - */ - function cachePush(value) { - var data = this.data; - if (typeof value == 'string' || isObject(value)) { - data.set.add(value); - } else { - data.hash[value] = true; - } - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates a new array joining `array` with `other`. - * - * @private - * @param {Array} array The array to join. - * @param {Array} other The other array to join. - * @returns {Array} Returns the new concatenated array. - */ - function arrayConcat(array, other) { - var index = -1, - length = array.length, - othIndex = -1, - othLength = other.length, - result = Array(length + othLength); - - while (++index < length) { - result[index] = array[index]; - } - while (++othIndex < othLength) { - result[index++] = other[othIndex]; - } - return result; - } - - /** - * Copies the values of `source` to `array`. - * - * @private - * @param {Array} source The array to copy values from. - * @param {Array} [array=[]] The array to copy values to. - * @returns {Array} Returns `array`. - */ - function arrayCopy(source, array) { - var index = -1, - length = source.length; - - array || (array = Array(length)); - while (++index < length) { - array[index] = source[index]; - } - return array; - } - - /** - * A specialized version of `_.forEach` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ - function arrayEach(array, iteratee) { - var index = -1, - length = array.length; - - while (++index < length) { - if (iteratee(array[index], index, array) === false) { - break; - } - } - return array; - } - - /** - * A specialized version of `_.forEachRight` for arrays without support for - * callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns `array`. - */ - function arrayEachRight(array, iteratee) { - var length = array.length; - - while (length--) { - if (iteratee(array[length], length, array) === false) { - break; - } - } - return array; - } - - /** - * A specialized version of `_.every` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - */ - function arrayEvery(array, predicate) { - var index = -1, - length = array.length; - - while (++index < length) { - if (!predicate(array[index], index, array)) { - return false; - } - } - return true; - } - - /** - * A specialized version of `baseExtremum` for arrays which invokes `iteratee` - * with one argument: (value). - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} comparator The function used to compare values. - * @param {*} exValue The initial extremum value. - * @returns {*} Returns the extremum value. - */ - function arrayExtremum(array, iteratee, comparator, exValue) { - var index = -1, - length = array.length, - computed = exValue, - result = computed; - - while (++index < length) { - var value = array[index], - current = +iteratee(value); - - if (comparator(current, computed)) { - computed = current; - result = value; - } - } - return result; - } - - /** - * A specialized version of `_.filter` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ - function arrayFilter(array, predicate) { - var index = -1, - length = array.length, - resIndex = -1, - result = []; - - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result[++resIndex] = value; - } - } - return result; - } - - /** - * A specialized version of `_.map` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ - function arrayMap(array, iteratee) { - var index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - result[index] = iteratee(array[index], index, array); - } - return result; - } - - /** - * Appends the elements of `values` to `array`. - * - * @private - * @param {Array} array The array to modify. - * @param {Array} values The values to append. - * @returns {Array} Returns `array`. - */ - function arrayPush(array, values) { - var index = -1, - length = values.length, - offset = array.length; - - while (++index < length) { - array[offset + index] = values[index]; - } - return array; - } - - /** - * A specialized version of `_.reduce` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {boolean} [initFromArray] Specify using the first element of `array` - * as the initial value. - * @returns {*} Returns the accumulated value. - */ - function arrayReduce(array, iteratee, accumulator, initFromArray) { - var index = -1, - length = array.length; - - if (initFromArray && length) { - accumulator = array[++index]; - } - while (++index < length) { - accumulator = iteratee(accumulator, array[index], index, array); - } - return accumulator; - } - - /** - * A specialized version of `_.reduceRight` for arrays without support for - * callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {boolean} [initFromArray] Specify using the last element of `array` - * as the initial value. - * @returns {*} Returns the accumulated value. - */ - function arrayReduceRight(array, iteratee, accumulator, initFromArray) { - var length = array.length; - if (initFromArray && length) { - accumulator = array[--length]; - } - while (length--) { - accumulator = iteratee(accumulator, array[length], length, array); - } - return accumulator; - } - - /** - * A specialized version of `_.some` for arrays without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ - function arraySome(array, predicate) { - var index = -1, - length = array.length; - - while (++index < length) { - if (predicate(array[index], index, array)) { - return true; - } - } - return false; - } - - /** - * A specialized version of `_.sum` for arrays without support for callback - * shorthands and `this` binding.. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the sum. - */ - function arraySum(array, iteratee) { - var length = array.length, - result = 0; - - while (length--) { - result += +iteratee(array[length]) || 0; - } - return result; - } - - /** - * Used by `_.defaults` to customize its `_.assign` use. - * - * @private - * @param {*} objectValue The destination object property value. - * @param {*} sourceValue The source object property value. - * @returns {*} Returns the value to assign to the destination object. - */ - function assignDefaults(objectValue, sourceValue) { - return objectValue === undefined ? sourceValue : objectValue; - } - - /** - * Used by `_.template` to customize its `_.assign` use. - * - * **Note:** This function is like `assignDefaults` except that it ignores - * inherited property values when checking if a property is `undefined`. - * - * @private - * @param {*} objectValue The destination object property value. - * @param {*} sourceValue The source object property value. - * @param {string} key The key associated with the object and source values. - * @param {Object} object The destination object. - * @returns {*} Returns the value to assign to the destination object. - */ - function assignOwnDefaults(objectValue, sourceValue, key, object) { - return (objectValue === undefined || !hasOwnProperty.call(object, key)) - ? sourceValue - : objectValue; - } - - /** - * A specialized version of `_.assign` for customizing assigned values without - * support for argument juggling, multiple sources, and `this` binding `customizer` - * functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {Function} customizer The function to customize assigned values. - * @returns {Object} Returns `object`. - */ - function assignWith(object, source, customizer) { - var index = -1, - props = keys(source), - length = props.length; - - while (++index < length) { - var key = props[index], - value = object[key], - result = customizer(value, source[key], key, object, source); - - if ((result === result ? (result !== value) : (value === value)) || - (value === undefined && !(key in object))) { - object[key] = result; - } - } - return object; - } - - /** - * The base implementation of `_.assign` without support for argument juggling, - * multiple sources, and `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @returns {Object} Returns `object`. - */ - function baseAssign(object, source) { - return source == null - ? object - : baseCopy(source, keys(source), object); - } - - /** - * The base implementation of `_.at` without support for string collections - * and individual key arguments. - * - * @private - * @param {Array|Object} collection The collection to iterate over. - * @param {number[]|string[]} props The property names or indexes of elements to pick. - * @returns {Array} Returns the new array of picked elements. - */ - function baseAt(collection, props) { - var index = -1, - isNil = collection == null, - isArr = !isNil && isArrayLike(collection), - length = isArr ? collection.length : 0, - propsLength = props.length, - result = Array(propsLength); - - while(++index < propsLength) { - var key = props[index]; - if (isArr) { - result[index] = isIndex(key, length) ? collection[key] : undefined; - } else { - result[index] = isNil ? undefined : collection[key]; - } - } - return result; - } - - /** - * Copies properties of `source` to `object`. - * - * @private - * @param {Object} source The object to copy properties from. - * @param {Array} props The property names to copy. - * @param {Object} [object={}] The object to copy properties to. - * @returns {Object} Returns `object`. - */ - function baseCopy(source, props, object) { - object || (object = {}); - - var index = -1, - length = props.length; - - while (++index < length) { - var key = props[index]; - object[key] = source[key]; - } - return object; - } - - /** - * The base implementation of `_.callback` which supports specifying the - * number of arguments to provide to `func`. - * - * @private - * @param {*} [func=_.identity] The value to convert to a callback. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {number} [argCount] The number of arguments to provide to `func`. - * @returns {Function} Returns the callback. - */ - function baseCallback(func, thisArg, argCount) { - var type = typeof func; - if (type == 'function') { - return thisArg === undefined - ? func - : bindCallback(func, thisArg, argCount); - } - if (func == null) { - return identity; - } - if (type == 'object') { - return baseMatches(func); - } - return thisArg === undefined - ? property(func) - : baseMatchesProperty(func, thisArg); - } - - /** - * The base implementation of `_.clone` without support for argument juggling - * and `this` binding `customizer` functions. - * - * @private - * @param {*} value The value to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @param {Function} [customizer] The function to customize cloning values. - * @param {string} [key] The key of `value`. - * @param {Object} [object] The object `value` belongs to. - * @param {Array} [stackA=[]] Tracks traversed source objects. - * @param {Array} [stackB=[]] Associates clones with source counterparts. - * @returns {*} Returns the cloned value. - */ - function baseClone(value, isDeep, customizer, key, object, stackA, stackB) { - var result; - if (customizer) { - result = object ? customizer(value, key, object) : customizer(value); - } - if (result !== undefined) { - return result; - } - if (!isObject(value)) { - return value; - } - var isArr = isArray(value); - if (isArr) { - result = initCloneArray(value); - if (!isDeep) { - return arrayCopy(value, result); - } - } else { - var tag = objToString.call(value), - isFunc = tag == funcTag; - - if (tag == objectTag || tag == argsTag || (isFunc && !object)) { - result = initCloneObject(isFunc ? {} : value); - if (!isDeep) { - return baseAssign(result, value); - } - } else { - return cloneableTags[tag] - ? initCloneByTag(value, tag, isDeep) - : (object ? value : {}); - } - } - // Check for circular references and return its corresponding clone. - stackA || (stackA = []); - stackB || (stackB = []); - - var length = stackA.length; - while (length--) { - if (stackA[length] == value) { - return stackB[length]; - } - } - // Add the source value to the stack of traversed objects and associate it with its clone. - stackA.push(value); - stackB.push(result); - - // Recursively populate clone (susceptible to call stack limits). - (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) { - result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB); - }); - return result; - } - - /** - * The base implementation of `_.create` without support for assigning - * properties to the created object. - * - * @private - * @param {Object} prototype The object to inherit from. - * @returns {Object} Returns the new object. - */ - var baseCreate = (function() { - function object() {} - return function(prototype) { - if (isObject(prototype)) { - object.prototype = prototype; - var result = new object; - object.prototype = undefined; - } - return result || {}; - }; - }()); - - /** - * The base implementation of `_.delay` and `_.defer` which accepts an index - * of where to slice the arguments to provide to `func`. - * - * @private - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {Object} args The arguments provide to `func`. - * @returns {number} Returns the timer id. - */ - function baseDelay(func, wait, args) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - return setTimeout(function() { func.apply(undefined, args); }, wait); - } - - /** - * The base implementation of `_.difference` which accepts a single array - * of values to exclude. - * - * @private - * @param {Array} array The array to inspect. - * @param {Array} values The values to exclude. - * @returns {Array} Returns the new array of filtered values. - */ - function baseDifference(array, values) { - var length = array ? array.length : 0, - result = []; - - if (!length) { - return result; - } - var index = -1, - indexOf = getIndexOf(), - isCommon = indexOf == baseIndexOf, - cache = (isCommon && values.length >= LARGE_ARRAY_SIZE) ? createCache(values) : null, - valuesLength = values.length; - - if (cache) { - indexOf = cacheIndexOf; - isCommon = false; - values = cache; - } - outer: - while (++index < length) { - var value = array[index]; - - if (isCommon && value === value) { - var valuesIndex = valuesLength; - while (valuesIndex--) { - if (values[valuesIndex] === value) { - continue outer; - } - } - result.push(value); - } - else if (indexOf(values, value, 0) < 0) { - result.push(value); - } - } - return result; - } - - /** - * The base implementation of `_.forEach` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object|string} Returns `collection`. - */ - var baseEach = createBaseEach(baseForOwn); - - /** - * The base implementation of `_.forEachRight` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object|string} Returns `collection`. - */ - var baseEachRight = createBaseEach(baseForOwnRight, true); - - /** - * The base implementation of `_.every` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false` - */ - function baseEvery(collection, predicate) { - var result = true; - baseEach(collection, function(value, index, collection) { - result = !!predicate(value, index, collection); - return result; - }); - return result; - } - - /** - * Gets the extremum value of `collection` invoking `iteratee` for each value - * in `collection` to generate the criterion by which the value is ranked. - * The `iteratee` is invoked with three arguments: (value, index|key, collection). - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} comparator The function used to compare values. - * @param {*} exValue The initial extremum value. - * @returns {*} Returns the extremum value. - */ - function baseExtremum(collection, iteratee, comparator, exValue) { - var computed = exValue, - result = computed; - - baseEach(collection, function(value, index, collection) { - var current = +iteratee(value, index, collection); - if (comparator(current, computed) || (current === exValue && current === result)) { - computed = current; - result = value; - } - }); - return result; - } - - /** - * The base implementation of `_.fill` without an iteratee call guard. - * - * @private - * @param {Array} array The array to fill. - * @param {*} value The value to fill `array` with. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns `array`. - */ - function baseFill(array, value, start, end) { - var length = array.length; - - start = start == null ? 0 : (+start || 0); - if (start < 0) { - start = -start > length ? 0 : (length + start); - } - end = (end === undefined || end > length) ? length : (+end || 0); - if (end < 0) { - end += length; - } - length = start > end ? 0 : (end >>> 0); - start >>>= 0; - - while (start < length) { - array[start++] = value; - } - return array; - } - - /** - * The base implementation of `_.filter` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - */ - function baseFilter(collection, predicate) { - var result = []; - baseEach(collection, function(value, index, collection) { - if (predicate(value, index, collection)) { - result.push(value); - } - }); - return result; - } - - /** - * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`, - * without support for callback shorthands and `this` binding, which iterates - * over `collection` using the provided `eachFunc`. - * - * @private - * @param {Array|Object|string} collection The collection to search. - * @param {Function} predicate The function invoked per iteration. - * @param {Function} eachFunc The function to iterate over `collection`. - * @param {boolean} [retKey] Specify returning the key of the found element - * instead of the element itself. - * @returns {*} Returns the found element or its key, else `undefined`. - */ - function baseFind(collection, predicate, eachFunc, retKey) { - var result; - eachFunc(collection, function(value, key, collection) { - if (predicate(value, key, collection)) { - result = retKey ? key : value; - return false; - } - }); - return result; - } - - /** - * The base implementation of `_.flatten` with added support for restricting - * flattening and specifying the start index. - * - * @private - * @param {Array} array The array to flatten. - * @param {boolean} [isDeep] Specify a deep flatten. - * @param {boolean} [isStrict] Restrict flattening to arrays-like objects. - * @param {Array} [result=[]] The initial result value. - * @returns {Array} Returns the new flattened array. - */ - function baseFlatten(array, isDeep, isStrict, result) { - result || (result = []); - - var index = -1, - length = array.length; - - while (++index < length) { - var value = array[index]; - if (isObjectLike(value) && isArrayLike(value) && - (isStrict || isArray(value) || isArguments(value))) { - if (isDeep) { - // Recursively flatten arrays (susceptible to call stack limits). - baseFlatten(value, isDeep, isStrict, result); - } else { - arrayPush(result, value); - } - } else if (!isStrict) { - result[result.length] = value; - } - } - return result; - } - - /** - * The base implementation of `baseForIn` and `baseForOwn` which iterates - * over `object` properties returned by `keysFunc` invoking `iteratee` for - * each property. Iteratee functions may exit iteration early by explicitly - * returning `false`. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ - var baseFor = createBaseFor(); - - /** - * This function is like `baseFor` except that it iterates over properties - * in the opposite order. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Object} Returns `object`. - */ - var baseForRight = createBaseFor(true); - - /** - * The base implementation of `_.forIn` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForIn(object, iteratee) { - return baseFor(object, iteratee, keysIn); - } - - /** - * The base implementation of `_.forOwn` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwn(object, iteratee) { - return baseFor(object, iteratee, keys); - } - - /** - * The base implementation of `_.forOwnRight` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - */ - function baseForOwnRight(object, iteratee) { - return baseForRight(object, iteratee, keys); - } - - /** - * The base implementation of `_.functions` which creates an array of - * `object` function property names filtered from those provided. - * - * @private - * @param {Object} object The object to inspect. - * @param {Array} props The property names to filter. - * @returns {Array} Returns the new array of filtered property names. - */ - function baseFunctions(object, props) { - var index = -1, - length = props.length, - resIndex = -1, - result = []; - - while (++index < length) { - var key = props[index]; - if (isFunction(object[key])) { - result[++resIndex] = key; - } - } - return result; - } - - /** - * The base implementation of `get` without support for string paths - * and default values. - * - * @private - * @param {Object} object The object to query. - * @param {Array} path The path of the property to get. - * @param {string} [pathKey] The key representation of path. - * @returns {*} Returns the resolved value. - */ - function baseGet(object, path, pathKey) { - if (object == null) { - return; - } - if (pathKey !== undefined && pathKey in toObject(object)) { - path = [pathKey]; - } - var index = 0, - length = path.length; - - while (object != null && index < length) { - object = object[path[index++]]; - } - return (index && index == length) ? object : undefined; - } - - /** - * The base implementation of `_.isEqual` without support for `this` binding - * `customizer` functions. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparing values. - * @param {boolean} [isLoose] Specify performing partial comparisons. - * @param {Array} [stackA] Tracks traversed `value` objects. - * @param {Array} [stackB] Tracks traversed `other` objects. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - */ - function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) { - if (value === other) { - return true; - } - if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { - return value !== value && other !== other; - } - return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB); - } - - /** - * A specialized version of `baseIsEqual` for arrays and objects which performs - * deep comparisons and tracks traversed objects enabling objects with circular - * references to be compared. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} [customizer] The function to customize comparing objects. - * @param {boolean} [isLoose] Specify performing partial comparisons. - * @param {Array} [stackA=[]] Tracks traversed `value` objects. - * @param {Array} [stackB=[]] Tracks traversed `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) { - var objIsArr = isArray(object), - othIsArr = isArray(other), - objTag = arrayTag, - othTag = arrayTag; - - if (!objIsArr) { - objTag = objToString.call(object); - if (objTag == argsTag) { - objTag = objectTag; - } else if (objTag != objectTag) { - objIsArr = isTypedArray(object); - } - } - if (!othIsArr) { - othTag = objToString.call(other); - if (othTag == argsTag) { - othTag = objectTag; - } else if (othTag != objectTag) { - othIsArr = isTypedArray(other); - } - } - var objIsObj = objTag == objectTag, - othIsObj = othTag == objectTag, - isSameTag = objTag == othTag; - - if (isSameTag && !(objIsArr || objIsObj)) { - return equalByTag(object, other, objTag); - } - if (!isLoose) { - var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), - othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); - - if (objIsWrapped || othIsWrapped) { - return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB); - } - } - if (!isSameTag) { - return false; - } - // Assume cyclic values are equal. - // For more information on detecting circular references see https://es5.github.io/#JO. - stackA || (stackA = []); - stackB || (stackB = []); - - var length = stackA.length; - while (length--) { - if (stackA[length] == object) { - return stackB[length] == other; - } - } - // Add `object` and `other` to the stack of traversed objects. - stackA.push(object); - stackB.push(other); - - var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB); - - stackA.pop(); - stackB.pop(); - - return result; - } - - /** - * The base implementation of `_.isMatch` without support for callback - * shorthands and `this` binding. - * - * @private - * @param {Object} object The object to inspect. - * @param {Array} matchData The propery names, values, and compare flags to match. - * @param {Function} [customizer] The function to customize comparing objects. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - */ - function baseIsMatch(object, matchData, customizer) { - var index = matchData.length, - length = index, - noCustomizer = !customizer; - - if (object == null) { - return !length; - } - object = toObject(object); - while (index--) { - var data = matchData[index]; - if ((noCustomizer && data[2]) - ? data[1] !== object[data[0]] - : !(data[0] in object) - ) { - return false; - } - } - while (++index < length) { - data = matchData[index]; - var key = data[0], - objValue = object[key], - srcValue = data[1]; - - if (noCustomizer && data[2]) { - if (objValue === undefined && !(key in object)) { - return false; - } - } else { - var result = customizer ? customizer(objValue, srcValue, key) : undefined; - if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) { - return false; - } - } - } - return true; - } - - /** - * The base implementation of `_.map` without support for callback shorthands - * and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - */ - function baseMap(collection, iteratee) { - var index = -1, - result = isArrayLike(collection) ? Array(collection.length) : []; - - baseEach(collection, function(value, key, collection) { - result[++index] = iteratee(value, key, collection); - }); - return result; - } - - /** - * The base implementation of `_.matches` which does not clone `source`. - * - * @private - * @param {Object} source The object of property values to match. - * @returns {Function} Returns the new function. - */ - function baseMatches(source) { - var matchData = getMatchData(source); - if (matchData.length == 1 && matchData[0][2]) { - var key = matchData[0][0], - value = matchData[0][1]; - - return function(object) { - if (object == null) { - return false; - } - return object[key] === value && (value !== undefined || (key in toObject(object))); - }; - } - return function(object) { - return baseIsMatch(object, matchData); - }; - } - - /** - * The base implementation of `_.matchesProperty` which does not clone `srcValue`. - * - * @private - * @param {string} path The path of the property to get. - * @param {*} srcValue The value to compare. - * @returns {Function} Returns the new function. - */ - function baseMatchesProperty(path, srcValue) { - var isArr = isArray(path), - isCommon = isKey(path) && isStrictComparable(srcValue), - pathKey = (path + ''); - - path = toPath(path); - return function(object) { - if (object == null) { - return false; - } - var key = pathKey; - object = toObject(object); - if ((isArr || !isCommon) && !(key in object)) { - object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); - if (object == null) { - return false; - } - key = last(path); - object = toObject(object); - } - return object[key] === srcValue - ? (srcValue !== undefined || (key in object)) - : baseIsEqual(srcValue, object[key], undefined, true); - }; - } - - /** - * The base implementation of `_.merge` without support for argument juggling, - * multiple sources, and `this` binding `customizer` functions. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {Function} [customizer] The function to customize merged values. - * @param {Array} [stackA=[]] Tracks traversed source objects. - * @param {Array} [stackB=[]] Associates values with source counterparts. - * @returns {Object} Returns `object`. - */ - function baseMerge(object, source, customizer, stackA, stackB) { - if (!isObject(object)) { - return object; - } - var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)), - props = isSrcArr ? undefined : keys(source); - - arrayEach(props || source, function(srcValue, key) { - if (props) { - key = srcValue; - srcValue = source[key]; - } - if (isObjectLike(srcValue)) { - stackA || (stackA = []); - stackB || (stackB = []); - baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); - } - else { - var value = object[key], - result = customizer ? customizer(value, srcValue, key, object, source) : undefined, - isCommon = result === undefined; - - if (isCommon) { - result = srcValue; - } - if ((result !== undefined || (isSrcArr && !(key in object))) && - (isCommon || (result === result ? (result !== value) : (value === value)))) { - object[key] = result; - } - } - }); - return object; - } - - /** - * A specialized version of `baseMerge` for arrays and objects which performs - * deep merges and tracks traversed objects enabling objects with circular - * references to be merged. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {string} key The key of the value to merge. - * @param {Function} mergeFunc The function to merge values. - * @param {Function} [customizer] The function to customize merged values. - * @param {Array} [stackA=[]] Tracks traversed source objects. - * @param {Array} [stackB=[]] Associates values with source counterparts. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) { - var length = stackA.length, - srcValue = source[key]; - - while (length--) { - if (stackA[length] == srcValue) { - object[key] = stackB[length]; - return; - } - } - var value = object[key], - result = customizer ? customizer(value, srcValue, key, object, source) : undefined, - isCommon = result === undefined; - - if (isCommon) { - result = srcValue; - if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) { - result = isArray(value) - ? value - : (isArrayLike(value) ? arrayCopy(value) : []); - } - else if (isPlainObject(srcValue) || isArguments(srcValue)) { - result = isArguments(value) - ? toPlainObject(value) - : (isPlainObject(value) ? value : {}); - } - else { - isCommon = false; - } - } - // Add the source value to the stack of traversed objects and associate - // it with its merged value. - stackA.push(srcValue); - stackB.push(result); - - if (isCommon) { - // Recursively merge objects and arrays (susceptible to call stack limits). - object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB); - } else if (result === result ? (result !== value) : (value === value)) { - object[key] = result; - } - } - - /** - * The base implementation of `_.property` without support for deep paths. - * - * @private - * @param {string} key The key of the property to get. - * @returns {Function} Returns the new function. - */ - function baseProperty(key) { - return function(object) { - return object == null ? undefined : object[key]; - }; - } - - /** - * A specialized version of `baseProperty` which supports deep paths. - * - * @private - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new function. - */ - function basePropertyDeep(path) { - var pathKey = (path + ''); - path = toPath(path); - return function(object) { - return baseGet(object, path, pathKey); - }; - } - - /** - * The base implementation of `_.pullAt` without support for individual - * index arguments and capturing the removed elements. - * - * @private - * @param {Array} array The array to modify. - * @param {number[]} indexes The indexes of elements to remove. - * @returns {Array} Returns `array`. - */ - function basePullAt(array, indexes) { - var length = array ? indexes.length : 0; - while (length--) { - var index = indexes[length]; - if (index != previous && isIndex(index)) { - var previous = index; - splice.call(array, index, 1); - } - } - return array; - } - - /** - * The base implementation of `_.random` without support for argument juggling - * and returning floating-point numbers. - * - * @private - * @param {number} min The minimum possible value. - * @param {number} max The maximum possible value. - * @returns {number} Returns the random number. - */ - function baseRandom(min, max) { - return min + nativeFloor(nativeRandom() * (max - min + 1)); - } - - /** - * The base implementation of `_.reduce` and `_.reduceRight` without support - * for callback shorthands and `this` binding, which iterates over `collection` - * using the provided `eachFunc`. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} accumulator The initial value. - * @param {boolean} initFromCollection Specify using the first or last element - * of `collection` as the initial value. - * @param {Function} eachFunc The function to iterate over `collection`. - * @returns {*} Returns the accumulated value. - */ - function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) { - eachFunc(collection, function(value, index, collection) { - accumulator = initFromCollection - ? (initFromCollection = false, value) - : iteratee(accumulator, value, index, collection); - }); - return accumulator; - } - - /** - * The base implementation of `setData` without support for hot loop detection. - * - * @private - * @param {Function} func The function to associate metadata with. - * @param {*} data The metadata. - * @returns {Function} Returns `func`. - */ - var baseSetData = !metaMap ? identity : function(func, data) { - metaMap.set(func, data); - return func; - }; - - /** - * The base implementation of `_.slice` without an iteratee call guard. - * - * @private - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the slice of `array`. - */ - function baseSlice(array, start, end) { - var index = -1, - length = array.length; - - start = start == null ? 0 : (+start || 0); - if (start < 0) { - start = -start > length ? 0 : (length + start); - } - end = (end === undefined || end > length) ? length : (+end || 0); - if (end < 0) { - end += length; - } - length = start > end ? 0 : ((end - start) >>> 0); - start >>>= 0; - - var result = Array(length); - while (++index < length) { - result[index] = array[index + start]; - } - return result; - } - - /** - * The base implementation of `_.some` without support for callback shorthands - * and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ - function baseSome(collection, predicate) { - var result; - - baseEach(collection, function(value, index, collection) { - result = predicate(value, index, collection); - return !result; - }); - return !!result; - } - - /** - * The base implementation of `_.sortBy` which uses `comparer` to define - * the sort order of `array` and replaces criteria objects with their - * corresponding values. - * - * @private - * @param {Array} array The array to sort. - * @param {Function} comparer The function to define sort order. - * @returns {Array} Returns `array`. - */ - function baseSortBy(array, comparer) { - var length = array.length; - - array.sort(comparer); - while (length--) { - array[length] = array[length].value; - } - return array; - } - - /** - * The base implementation of `_.sortByOrder` without param guards. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. - * @param {boolean[]} orders The sort orders of `iteratees`. - * @returns {Array} Returns the new sorted array. - */ - function baseSortByOrder(collection, iteratees, orders) { - var callback = getCallback(), - index = -1; - - iteratees = arrayMap(iteratees, function(iteratee) { return callback(iteratee); }); - - var result = baseMap(collection, function(value) { - var criteria = arrayMap(iteratees, function(iteratee) { return iteratee(value); }); - return { 'criteria': criteria, 'index': ++index, 'value': value }; - }); - - return baseSortBy(result, function(object, other) { - return compareMultiple(object, other, orders); - }); - } - - /** - * The base implementation of `_.sum` without support for callback shorthands - * and `this` binding. - * - * @private - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the sum. - */ - function baseSum(collection, iteratee) { - var result = 0; - baseEach(collection, function(value, index, collection) { - result += +iteratee(value, index, collection) || 0; - }); - return result; - } - - /** - * The base implementation of `_.uniq` without support for callback shorthands - * and `this` binding. - * - * @private - * @param {Array} array The array to inspect. - * @param {Function} [iteratee] The function invoked per iteration. - * @returns {Array} Returns the new duplicate-value-free array. - */ - function baseUniq(array, iteratee) { - var index = -1, - indexOf = getIndexOf(), - length = array.length, - isCommon = indexOf == baseIndexOf, - isLarge = isCommon && length >= LARGE_ARRAY_SIZE, - seen = isLarge ? createCache() : null, - result = []; - - if (seen) { - indexOf = cacheIndexOf; - isCommon = false; - } else { - isLarge = false; - seen = iteratee ? [] : result; - } - outer: - while (++index < length) { - var value = array[index], - computed = iteratee ? iteratee(value, index, array) : value; - - if (isCommon && value === value) { - var seenIndex = seen.length; - while (seenIndex--) { - if (seen[seenIndex] === computed) { - continue outer; - } - } - if (iteratee) { - seen.push(computed); - } - result.push(value); - } - else if (indexOf(seen, computed, 0) < 0) { - if (iteratee || isLarge) { - seen.push(computed); - } - result.push(value); - } - } - return result; - } - - /** - * The base implementation of `_.values` and `_.valuesIn` which creates an - * array of `object` property values corresponding to the property names - * of `props`. - * - * @private - * @param {Object} object The object to query. - * @param {Array} props The property names to get values for. - * @returns {Object} Returns the array of property values. - */ - function baseValues(object, props) { - var index = -1, - length = props.length, - result = Array(length); - - while (++index < length) { - result[index] = object[props[index]]; - } - return result; - } - - /** - * The base implementation of `_.dropRightWhile`, `_.dropWhile`, `_.takeRightWhile`, - * and `_.takeWhile` without support for callback shorthands and `this` binding. - * - * @private - * @param {Array} array The array to query. - * @param {Function} predicate The function invoked per iteration. - * @param {boolean} [isDrop] Specify dropping elements instead of taking them. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Array} Returns the slice of `array`. - */ - function baseWhile(array, predicate, isDrop, fromRight) { - var length = array.length, - index = fromRight ? length : -1; - - while ((fromRight ? index-- : ++index < length) && predicate(array[index], index, array)) {} - return isDrop - ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) - : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); - } - - /** - * The base implementation of `wrapperValue` which returns the result of - * performing a sequence of actions on the unwrapped `value`, where each - * successive action is supplied the return value of the previous. - * - * @private - * @param {*} value The unwrapped value. - * @param {Array} actions Actions to peform to resolve the unwrapped value. - * @returns {*} Returns the resolved value. - */ - function baseWrapperValue(value, actions) { - var result = value; - if (result instanceof LazyWrapper) { - result = result.value(); - } - var index = -1, - length = actions.length; - - while (++index < length) { - var action = actions[index]; - result = action.func.apply(action.thisArg, arrayPush([result], action.args)); - } - return result; - } - - /** - * Performs a binary search of `array` to determine the index at which `value` - * should be inserted into `array` in order to maintain its sort order. - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ - function binaryIndex(array, value, retHighest) { - var low = 0, - high = array ? array.length : low; - - if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { - while (low < high) { - var mid = (low + high) >>> 1, - computed = array[mid]; - - if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) { - low = mid + 1; - } else { - high = mid; - } - } - return high; - } - return binaryIndexBy(array, value, identity, retHighest); - } - - /** - * This function is like `binaryIndex` except that it invokes `iteratee` for - * `value` and each element of `array` to compute their sort ranking. The - * iteratee is invoked with one argument; (value). - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} iteratee The function invoked per iteration. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ - function binaryIndexBy(array, value, iteratee, retHighest) { - value = iteratee(value); - - var low = 0, - high = array ? array.length : 0, - valIsNaN = value !== value, - valIsNull = value === null, - valIsUndef = value === undefined; - - while (low < high) { - var mid = nativeFloor((low + high) / 2), - computed = iteratee(array[mid]), - isDef = computed !== undefined, - isReflexive = computed === computed; - - if (valIsNaN) { - var setLow = isReflexive || retHighest; - } else if (valIsNull) { - setLow = isReflexive && isDef && (retHighest || computed != null); - } else if (valIsUndef) { - setLow = isReflexive && (retHighest || isDef); - } else if (computed == null) { - setLow = false; - } else { - setLow = retHighest ? (computed <= value) : (computed < value); - } - if (setLow) { - low = mid + 1; - } else { - high = mid; - } - } - return nativeMin(high, MAX_ARRAY_INDEX); - } - - /** - * A specialized version of `baseCallback` which only supports `this` binding - * and specifying the number of arguments to provide to `func`. - * - * @private - * @param {Function} func The function to bind. - * @param {*} thisArg The `this` binding of `func`. - * @param {number} [argCount] The number of arguments to provide to `func`. - * @returns {Function} Returns the callback. - */ - function bindCallback(func, thisArg, argCount) { - if (typeof func != 'function') { - return identity; - } - if (thisArg === undefined) { - return func; - } - switch (argCount) { - case 1: return function(value) { - return func.call(thisArg, value); - }; - case 3: return function(value, index, collection) { - return func.call(thisArg, value, index, collection); - }; - case 4: return function(accumulator, value, index, collection) { - return func.call(thisArg, accumulator, value, index, collection); - }; - case 5: return function(value, other, key, object, source) { - return func.call(thisArg, value, other, key, object, source); - }; - } - return function() { - return func.apply(thisArg, arguments); - }; - } - - /** - * Creates a clone of the given array buffer. - * - * @private - * @param {ArrayBuffer} buffer The array buffer to clone. - * @returns {ArrayBuffer} Returns the cloned array buffer. - */ - function bufferClone(buffer) { - var result = new ArrayBuffer(buffer.byteLength), - view = new Uint8Array(result); - - view.set(new Uint8Array(buffer)); - return result; - } - - /** - * Creates an array that is the composition of partially applied arguments, - * placeholders, and provided arguments into a single array of arguments. - * - * @private - * @param {Array|Object} args The provided arguments. - * @param {Array} partials The arguments to prepend to those provided. - * @param {Array} holders The `partials` placeholder indexes. - * @returns {Array} Returns the new array of composed arguments. - */ - function composeArgs(args, partials, holders) { - var holdersLength = holders.length, - argsIndex = -1, - argsLength = nativeMax(args.length - holdersLength, 0), - leftIndex = -1, - leftLength = partials.length, - result = Array(leftLength + argsLength); - - while (++leftIndex < leftLength) { - result[leftIndex] = partials[leftIndex]; - } - while (++argsIndex < holdersLength) { - result[holders[argsIndex]] = args[argsIndex]; - } - while (argsLength--) { - result[leftIndex++] = args[argsIndex++]; - } - return result; - } - - /** - * This function is like `composeArgs` except that the arguments composition - * is tailored for `_.partialRight`. - * - * @private - * @param {Array|Object} args The provided arguments. - * @param {Array} partials The arguments to append to those provided. - * @param {Array} holders The `partials` placeholder indexes. - * @returns {Array} Returns the new array of composed arguments. - */ - function composeArgsRight(args, partials, holders) { - var holdersIndex = -1, - holdersLength = holders.length, - argsIndex = -1, - argsLength = nativeMax(args.length - holdersLength, 0), - rightIndex = -1, - rightLength = partials.length, - result = Array(argsLength + rightLength); - - while (++argsIndex < argsLength) { - result[argsIndex] = args[argsIndex]; - } - var offset = argsIndex; - while (++rightIndex < rightLength) { - result[offset + rightIndex] = partials[rightIndex]; - } - while (++holdersIndex < holdersLength) { - result[offset + holders[holdersIndex]] = args[argsIndex++]; - } - return result; - } - - /** - * Creates a `_.countBy`, `_.groupBy`, `_.indexBy`, or `_.partition` function. - * - * @private - * @param {Function} setter The function to set keys and values of the accumulator object. - * @param {Function} [initializer] The function to initialize the accumulator object. - * @returns {Function} Returns the new aggregator function. - */ - function createAggregator(setter, initializer) { - return function(collection, iteratee, thisArg) { - var result = initializer ? initializer() : {}; - iteratee = getCallback(iteratee, thisArg, 3); - - if (isArray(collection)) { - var index = -1, - length = collection.length; - - while (++index < length) { - var value = collection[index]; - setter(result, value, iteratee(value, index, collection), collection); - } - } else { - baseEach(collection, function(value, key, collection) { - setter(result, value, iteratee(value, key, collection), collection); - }); - } - return result; - }; - } - - /** - * Creates a `_.assign`, `_.defaults`, or `_.merge` function. - * - * @private - * @param {Function} assigner The function to assign values. - * @returns {Function} Returns the new assigner function. - */ - function createAssigner(assigner) { - return restParam(function(object, sources) { - var index = -1, - length = object == null ? 0 : sources.length, - customizer = length > 2 ? sources[length - 2] : undefined, - guard = length > 2 ? sources[2] : undefined, - thisArg = length > 1 ? sources[length - 1] : undefined; - - if (typeof customizer == 'function') { - customizer = bindCallback(customizer, thisArg, 5); - length -= 2; - } else { - customizer = typeof thisArg == 'function' ? thisArg : undefined; - length -= (customizer ? 1 : 0); - } - if (guard && isIterateeCall(sources[0], sources[1], guard)) { - customizer = length < 3 ? undefined : customizer; - length = 1; - } - while (++index < length) { - var source = sources[index]; - if (source) { - assigner(object, source, customizer); - } - } - return object; - }); - } - - /** - * Creates a `baseEach` or `baseEachRight` function. - * - * @private - * @param {Function} eachFunc The function to iterate over a collection. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ - function createBaseEach(eachFunc, fromRight) { - return function(collection, iteratee) { - var length = collection ? getLength(collection) : 0; - if (!isLength(length)) { - return eachFunc(collection, iteratee); - } - var index = fromRight ? length : -1, - iterable = toObject(collection); - - while ((fromRight ? index-- : ++index < length)) { - if (iteratee(iterable[index], index, iterable) === false) { - break; - } - } - return collection; - }; - } - - /** - * Creates a base function for `_.forIn` or `_.forInRight`. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new base function. - */ - function createBaseFor(fromRight) { - return function(object, iteratee, keysFunc) { - var iterable = toObject(object), - props = keysFunc(object), - length = props.length, - index = fromRight ? length : -1; - - while ((fromRight ? index-- : ++index < length)) { - var key = props[index]; - if (iteratee(iterable[key], key, iterable) === false) { - break; - } - } - return object; - }; - } - - /** - * Creates a function that wraps `func` and invokes it with the `this` - * binding of `thisArg`. - * - * @private - * @param {Function} func The function to bind. - * @param {*} [thisArg] The `this` binding of `func`. - * @returns {Function} Returns the new bound function. - */ - function createBindWrapper(func, thisArg) { - var Ctor = createCtorWrapper(func); - - function wrapper() { - var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; - return fn.apply(thisArg, arguments); - } - return wrapper; - } - - /** - * Creates a `Set` cache object to optimize linear searches of large arrays. - * - * @private - * @param {Array} [values] The values to cache. - * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`. - */ - function createCache(values) { - return (nativeCreate && Set) ? new SetCache(values) : null; - } - - /** - * Creates a function that produces compound words out of the words in a - * given string. - * - * @private - * @param {Function} callback The function to combine each word. - * @returns {Function} Returns the new compounder function. - */ - function createCompounder(callback) { - return function(string) { - var index = -1, - array = words(deburr(string)), - length = array.length, - result = ''; - - while (++index < length) { - result = callback(result, array[index], index); - } - return result; - }; - } - - /** - * Creates a function that produces an instance of `Ctor` regardless of - * whether it was invoked as part of a `new` expression or by `call` or `apply`. - * - * @private - * @param {Function} Ctor The constructor to wrap. - * @returns {Function} Returns the new wrapped function. - */ - function createCtorWrapper(Ctor) { - return function() { - // Use a `switch` statement to work with class constructors. - // See http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist - // for more details. - var args = arguments; - switch (args.length) { - case 0: return new Ctor; - case 1: return new Ctor(args[0]); - case 2: return new Ctor(args[0], args[1]); - case 3: return new Ctor(args[0], args[1], args[2]); - case 4: return new Ctor(args[0], args[1], args[2], args[3]); - case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); - case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); - case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - } - var thisBinding = baseCreate(Ctor.prototype), - result = Ctor.apply(thisBinding, args); - - // Mimic the constructor's `return` behavior. - // See https://es5.github.io/#x13.2.2 for more details. - return isObject(result) ? result : thisBinding; - }; - } - - /** - * Creates a `_.curry` or `_.curryRight` function. - * - * @private - * @param {boolean} flag The curry bit flag. - * @returns {Function} Returns the new curry function. - */ - function createCurry(flag) { - function curryFunc(func, arity, guard) { - if (guard && isIterateeCall(func, arity, guard)) { - arity = undefined; - } - var result = createWrapper(func, flag, undefined, undefined, undefined, undefined, undefined, arity); - result.placeholder = curryFunc.placeholder; - return result; - } - return curryFunc; - } - - /** - * Creates a `_.defaults` or `_.defaultsDeep` function. - * - * @private - * @param {Function} assigner The function to assign values. - * @param {Function} customizer The function to customize assigned values. - * @returns {Function} Returns the new defaults function. - */ - function createDefaults(assigner, customizer) { - return restParam(function(args) { - var object = args[0]; - if (object == null) { - return object; - } - args.push(customizer); - return assigner.apply(undefined, args); - }); - } - - /** - * Creates a `_.max` or `_.min` function. - * - * @private - * @param {Function} comparator The function used to compare values. - * @param {*} exValue The initial extremum value. - * @returns {Function} Returns the new extremum function. - */ - function createExtremum(comparator, exValue) { - return function(collection, iteratee, thisArg) { - if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { - iteratee = undefined; - } - iteratee = getCallback(iteratee, thisArg, 3); - if (iteratee.length == 1) { - collection = isArray(collection) ? collection : toIterable(collection); - var result = arrayExtremum(collection, iteratee, comparator, exValue); - if (!(collection.length && result === exValue)) { - return result; - } - } - return baseExtremum(collection, iteratee, comparator, exValue); - }; - } - - /** - * Creates a `_.find` or `_.findLast` function. - * - * @private - * @param {Function} eachFunc The function to iterate over a collection. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new find function. - */ - function createFind(eachFunc, fromRight) { - return function(collection, predicate, thisArg) { - predicate = getCallback(predicate, thisArg, 3); - if (isArray(collection)) { - var index = baseFindIndex(collection, predicate, fromRight); - return index > -1 ? collection[index] : undefined; - } - return baseFind(collection, predicate, eachFunc); - }; - } - - /** - * Creates a `_.findIndex` or `_.findLastIndex` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new find function. - */ - function createFindIndex(fromRight) { - return function(array, predicate, thisArg) { - if (!(array && array.length)) { - return -1; - } - predicate = getCallback(predicate, thisArg, 3); - return baseFindIndex(array, predicate, fromRight); - }; - } - - /** - * Creates a `_.findKey` or `_.findLastKey` function. - * - * @private - * @param {Function} objectFunc The function to iterate over an object. - * @returns {Function} Returns the new find function. - */ - function createFindKey(objectFunc) { - return function(object, predicate, thisArg) { - predicate = getCallback(predicate, thisArg, 3); - return baseFind(object, predicate, objectFunc, true); - }; - } - - /** - * Creates a `_.flow` or `_.flowRight` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new flow function. - */ - function createFlow(fromRight) { - return function() { - var wrapper, - length = arguments.length, - index = fromRight ? length : -1, - leftIndex = 0, - funcs = Array(length); - - while ((fromRight ? index-- : ++index < length)) { - var func = funcs[leftIndex++] = arguments[index]; - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - if (!wrapper && LodashWrapper.prototype.thru && getFuncName(func) == 'wrapper') { - wrapper = new LodashWrapper([], true); - } - } - index = wrapper ? -1 : length; - while (++index < length) { - func = funcs[index]; - - var funcName = getFuncName(func), - data = funcName == 'wrapper' ? getData(func) : undefined; - - if (data && isLaziable(data[0]) && data[1] == (ARY_FLAG | CURRY_FLAG | PARTIAL_FLAG | REARG_FLAG) && !data[4].length && data[9] == 1) { - wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); - } else { - wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func); - } - } - return function() { - var args = arguments, - value = args[0]; - - if (wrapper && args.length == 1 && isArray(value) && value.length >= LARGE_ARRAY_SIZE) { - return wrapper.plant(value).value(); - } - var index = 0, - result = length ? funcs[index].apply(this, args) : value; - - while (++index < length) { - result = funcs[index].call(this, result); - } - return result; - }; - }; - } - - /** - * Creates a function for `_.forEach` or `_.forEachRight`. - * - * @private - * @param {Function} arrayFunc The function to iterate over an array. - * @param {Function} eachFunc The function to iterate over a collection. - * @returns {Function} Returns the new each function. - */ - function createForEach(arrayFunc, eachFunc) { - return function(collection, iteratee, thisArg) { - return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) - ? arrayFunc(collection, iteratee) - : eachFunc(collection, bindCallback(iteratee, thisArg, 3)); - }; - } - - /** - * Creates a function for `_.forIn` or `_.forInRight`. - * - * @private - * @param {Function} objectFunc The function to iterate over an object. - * @returns {Function} Returns the new each function. - */ - function createForIn(objectFunc) { - return function(object, iteratee, thisArg) { - if (typeof iteratee != 'function' || thisArg !== undefined) { - iteratee = bindCallback(iteratee, thisArg, 3); - } - return objectFunc(object, iteratee, keysIn); - }; - } - - /** - * Creates a function for `_.forOwn` or `_.forOwnRight`. - * - * @private - * @param {Function} objectFunc The function to iterate over an object. - * @returns {Function} Returns the new each function. - */ - function createForOwn(objectFunc) { - return function(object, iteratee, thisArg) { - if (typeof iteratee != 'function' || thisArg !== undefined) { - iteratee = bindCallback(iteratee, thisArg, 3); - } - return objectFunc(object, iteratee); - }; - } - - /** - * Creates a function for `_.mapKeys` or `_.mapValues`. - * - * @private - * @param {boolean} [isMapKeys] Specify mapping keys instead of values. - * @returns {Function} Returns the new map function. - */ - function createObjectMapper(isMapKeys) { - return function(object, iteratee, thisArg) { - var result = {}; - iteratee = getCallback(iteratee, thisArg, 3); - - baseForOwn(object, function(value, key, object) { - var mapped = iteratee(value, key, object); - key = isMapKeys ? mapped : key; - value = isMapKeys ? value : mapped; - result[key] = value; - }); - return result; - }; - } - - /** - * Creates a function for `_.padLeft` or `_.padRight`. - * - * @private - * @param {boolean} [fromRight] Specify padding from the right. - * @returns {Function} Returns the new pad function. - */ - function createPadDir(fromRight) { - return function(string, length, chars) { - string = baseToString(string); - return (fromRight ? string : '') + createPadding(string, length, chars) + (fromRight ? '' : string); - }; - } - - /** - * Creates a `_.partial` or `_.partialRight` function. - * - * @private - * @param {boolean} flag The partial bit flag. - * @returns {Function} Returns the new partial function. - */ - function createPartial(flag) { - var partialFunc = restParam(function(func, partials) { - var holders = replaceHolders(partials, partialFunc.placeholder); - return createWrapper(func, flag, undefined, partials, holders); - }); - return partialFunc; - } - - /** - * Creates a function for `_.reduce` or `_.reduceRight`. - * - * @private - * @param {Function} arrayFunc The function to iterate over an array. - * @param {Function} eachFunc The function to iterate over a collection. - * @returns {Function} Returns the new each function. - */ - function createReduce(arrayFunc, eachFunc) { - return function(collection, iteratee, accumulator, thisArg) { - var initFromArray = arguments.length < 3; - return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) - ? arrayFunc(collection, iteratee, accumulator, initFromArray) - : baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc); - }; - } - - /** - * Creates a function that wraps `func` and invokes it with optional `this` - * binding of, partial application, and currying. - * - * @private - * @param {Function|string} func The function or method name to reference. - * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to prepend to those provided to the new function. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [partialsRight] The arguments to append to those provided to the new function. - * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { - var isAry = bitmask & ARY_FLAG, - isBind = bitmask & BIND_FLAG, - isBindKey = bitmask & BIND_KEY_FLAG, - isCurry = bitmask & CURRY_FLAG, - isCurryBound = bitmask & CURRY_BOUND_FLAG, - isCurryRight = bitmask & CURRY_RIGHT_FLAG, - Ctor = isBindKey ? undefined : createCtorWrapper(func); - - function wrapper() { - // Avoid `arguments` object use disqualifying optimizations by - // converting it to an array before providing it to other functions. - var length = arguments.length, - index = length, - args = Array(length); - - while (index--) { - args[index] = arguments[index]; - } - if (partials) { - args = composeArgs(args, partials, holders); - } - if (partialsRight) { - args = composeArgsRight(args, partialsRight, holdersRight); - } - if (isCurry || isCurryRight) { - var placeholder = wrapper.placeholder, - argsHolders = replaceHolders(args, placeholder); - - length -= argsHolders.length; - if (length < arity) { - var newArgPos = argPos ? arrayCopy(argPos) : undefined, - newArity = nativeMax(arity - length, 0), - newsHolders = isCurry ? argsHolders : undefined, - newHoldersRight = isCurry ? undefined : argsHolders, - newPartials = isCurry ? args : undefined, - newPartialsRight = isCurry ? undefined : args; - - bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG); - bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG); - - if (!isCurryBound) { - bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); - } - var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity], - result = createHybridWrapper.apply(undefined, newData); - - if (isLaziable(func)) { - setData(result, newData); - } - result.placeholder = placeholder; - return result; - } - } - var thisBinding = isBind ? thisArg : this, - fn = isBindKey ? thisBinding[func] : func; - - if (argPos) { - args = reorder(args, argPos); - } - if (isAry && ary < args.length) { - args.length = ary; - } - if (this && this !== root && this instanceof wrapper) { - fn = Ctor || createCtorWrapper(func); - } - return fn.apply(thisBinding, args); - } - return wrapper; - } - - /** - * Creates the padding required for `string` based on the given `length`. - * The `chars` string is truncated if the number of characters exceeds `length`. - * - * @private - * @param {string} string The string to create padding for. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the pad for `string`. - */ - function createPadding(string, length, chars) { - var strLength = string.length; - length = +length; - - if (strLength >= length || !nativeIsFinite(length)) { - return ''; - } - var padLength = length - strLength; - chars = chars == null ? ' ' : (chars + ''); - return repeat(chars, nativeCeil(padLength / chars.length)).slice(0, padLength); - } - - /** - * Creates a function that wraps `func` and invokes it with the optional `this` - * binding of `thisArg` and the `partials` prepended to those provided to - * the wrapper. - * - * @private - * @param {Function} func The function to partially apply arguments to. - * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. - * @param {*} thisArg The `this` binding of `func`. - * @param {Array} partials The arguments to prepend to those provided to the new function. - * @returns {Function} Returns the new bound function. - */ - function createPartialWrapper(func, bitmask, thisArg, partials) { - var isBind = bitmask & BIND_FLAG, - Ctor = createCtorWrapper(func); - - function wrapper() { - // Avoid `arguments` object use disqualifying optimizations by - // converting it to an array before providing it `func`. - var argsIndex = -1, - argsLength = arguments.length, - leftIndex = -1, - leftLength = partials.length, - args = Array(leftLength + argsLength); - - while (++leftIndex < leftLength) { - args[leftIndex] = partials[leftIndex]; - } - while (argsLength--) { - args[leftIndex++] = arguments[++argsIndex]; - } - var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; - return fn.apply(isBind ? thisArg : this, args); - } - return wrapper; - } - - /** - * Creates a `_.ceil`, `_.floor`, or `_.round` function. - * - * @private - * @param {string} methodName The name of the `Math` method to use when rounding. - * @returns {Function} Returns the new round function. - */ - function createRound(methodName) { - var func = Math[methodName]; - return function(number, precision) { - precision = precision === undefined ? 0 : (+precision || 0); - if (precision) { - precision = pow(10, precision); - return func(number * precision) / precision; - } - return func(number); - }; - } - - /** - * Creates a `_.sortedIndex` or `_.sortedLastIndex` function. - * - * @private - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {Function} Returns the new index function. - */ - function createSortedIndex(retHighest) { - return function(array, value, iteratee, thisArg) { - var callback = getCallback(iteratee); - return (iteratee == null && callback === baseCallback) - ? binaryIndex(array, value, retHighest) - : binaryIndexBy(array, value, callback(iteratee, thisArg, 1), retHighest); - }; - } - - /** - * Creates a function that either curries or invokes `func` with optional - * `this` binding and partially applied arguments. - * - * @private - * @param {Function|string} func The function or method name to reference. - * @param {number} bitmask The bitmask of flags. - * The bitmask may be composed of the following flags: - * 1 - `_.bind` - * 2 - `_.bindKey` - * 4 - `_.curry` or `_.curryRight` of a bound function - * 8 - `_.curry` - * 16 - `_.curryRight` - * 32 - `_.partial` - * 64 - `_.partialRight` - * 128 - `_.rearg` - * 256 - `_.ary` - * @param {*} [thisArg] The `this` binding of `func`. - * @param {Array} [partials] The arguments to be partially applied. - * @param {Array} [holders] The `partials` placeholder indexes. - * @param {Array} [argPos] The argument positions of the new function. - * @param {number} [ary] The arity cap of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new wrapped function. - */ - function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { - var isBindKey = bitmask & BIND_KEY_FLAG; - if (!isBindKey && typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - var length = partials ? partials.length : 0; - if (!length) { - bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG); - partials = holders = undefined; - } - length -= (holders ? holders.length : 0); - if (bitmask & PARTIAL_RIGHT_FLAG) { - var partialsRight = partials, - holdersRight = holders; - - partials = holders = undefined; - } - var data = isBindKey ? undefined : getData(func), - newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; - - if (data) { - mergeData(newData, data); - bitmask = newData[1]; - arity = newData[9]; - } - newData[9] = arity == null - ? (isBindKey ? 0 : func.length) - : (nativeMax(arity - length, 0) || 0); - - if (bitmask == BIND_FLAG) { - var result = createBindWrapper(newData[0], newData[2]); - } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) { - result = createPartialWrapper.apply(undefined, newData); - } else { - result = createHybridWrapper.apply(undefined, newData); - } - var setter = data ? baseSetData : setData; - return setter(result, newData); - } - - /** - * A specialized version of `baseIsEqualDeep` for arrays with support for - * partial deep comparisons. - * - * @private - * @param {Array} array The array to compare. - * @param {Array} other The other array to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} [customizer] The function to customize comparing arrays. - * @param {boolean} [isLoose] Specify performing partial comparisons. - * @param {Array} [stackA] Tracks traversed `value` objects. - * @param {Array} [stackB] Tracks traversed `other` objects. - * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. - */ - function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) { - var index = -1, - arrLength = array.length, - othLength = other.length; - - if (arrLength != othLength && !(isLoose && othLength > arrLength)) { - return false; - } - // Ignore non-index properties. - while (++index < arrLength) { - var arrValue = array[index], - othValue = other[index], - result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined; - - if (result !== undefined) { - if (result) { - continue; - } - return false; - } - // Recursively compare arrays (susceptible to call stack limits). - if (isLoose) { - if (!arraySome(other, function(othValue) { - return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB); - })) { - return false; - } - } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) { - return false; - } - } - return true; - } - - /** - * A specialized version of `baseIsEqualDeep` for comparing objects of - * the same `toStringTag`. - * - * **Note:** This function only supports comparing values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {string} tag The `toStringTag` of the objects to compare. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalByTag(object, other, tag) { - switch (tag) { - case boolTag: - case dateTag: - // Coerce dates and booleans to numbers, dates to milliseconds and booleans - // to `1` or `0` treating invalid dates coerced to `NaN` as not equal. - return +object == +other; - - case errorTag: - return object.name == other.name && object.message == other.message; - - case numberTag: - // Treat `NaN` vs. `NaN` as equal. - return (object != +object) - ? other != +other - : object == +other; - - case regexpTag: - case stringTag: - // Coerce regexes to strings and treat strings primitives and string - // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details. - return object == (other + ''); - } - return false; - } - - /** - * A specialized version of `baseIsEqualDeep` for objects with support for - * partial deep comparisons. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} [customizer] The function to customize comparing values. - * @param {boolean} [isLoose] Specify performing partial comparisons. - * @param {Array} [stackA] Tracks traversed `value` objects. - * @param {Array} [stackB] Tracks traversed `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) { - var objProps = keys(object), - objLength = objProps.length, - othProps = keys(other), - othLength = othProps.length; - - if (objLength != othLength && !isLoose) { - return false; - } - var index = objLength; - while (index--) { - var key = objProps[index]; - if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) { - return false; - } - } - var skipCtor = isLoose; - while (++index < objLength) { - key = objProps[index]; - var objValue = object[key], - othValue = other[key], - result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined; - - // Recursively compare objects (susceptible to call stack limits). - if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) { - return false; - } - skipCtor || (skipCtor = key == 'constructor'); - } - if (!skipCtor) { - var objCtor = object.constructor, - othCtor = other.constructor; - - // Non `Object` object instances with different constructors are not equal. - if (objCtor != othCtor && - ('constructor' in object && 'constructor' in other) && - !(typeof objCtor == 'function' && objCtor instanceof objCtor && - typeof othCtor == 'function' && othCtor instanceof othCtor)) { - return false; - } - } - return true; - } - - /** - * Gets the appropriate "callback" function. If the `_.callback` method is - * customized this function returns the custom method, otherwise it returns - * the `baseCallback` function. If arguments are provided the chosen function - * is invoked with them and its result is returned. - * - * @private - * @returns {Function} Returns the chosen function or its result. - */ - function getCallback(func, thisArg, argCount) { - var result = lodash.callback || callback; - result = result === callback ? baseCallback : result; - return argCount ? result(func, thisArg, argCount) : result; - } - - /** - * Gets metadata for `func`. - * - * @private - * @param {Function} func The function to query. - * @returns {*} Returns the metadata for `func`. - */ - var getData = !metaMap ? noop : function(func) { - return metaMap.get(func); - }; - - /** - * Gets the name of `func`. - * - * @private - * @param {Function} func The function to query. - * @returns {string} Returns the function name. - */ - function getFuncName(func) { - var result = func.name, - array = realNames[result], - length = array ? array.length : 0; - - while (length--) { - var data = array[length], - otherFunc = data.func; - if (otherFunc == null || otherFunc == func) { - return data.name; - } - } - return result; - } - - /** - * Gets the appropriate "indexOf" function. If the `_.indexOf` method is - * customized this function returns the custom method, otherwise it returns - * the `baseIndexOf` function. If arguments are provided the chosen function - * is invoked with them and its result is returned. - * - * @private - * @returns {Function|number} Returns the chosen function or its result. - */ - function getIndexOf(collection, target, fromIndex) { - var result = lodash.indexOf || indexOf; - result = result === indexOf ? baseIndexOf : result; - return collection ? result(collection, target, fromIndex) : result; - } - - /** - * Gets the "length" property value of `object`. - * - * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) - * that affects Safari on at least iOS 8.1-8.3 ARM64. - * - * @private - * @param {Object} object The object to query. - * @returns {*} Returns the "length" value. - */ - var getLength = baseProperty('length'); - - /** - * Gets the propery names, values, and compare flags of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the match data of `object`. - */ - function getMatchData(object) { - var result = pairs(object), - length = result.length; - - while (length--) { - result[length][2] = isStrictComparable(result[length][1]); - } - return result; - } - - /** - * Gets the native function at `key` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. - */ - function getNative(object, key) { - var value = object == null ? undefined : object[key]; - return isNative(value) ? value : undefined; - } - - /** - * Gets the view, applying any `transforms` to the `start` and `end` positions. - * - * @private - * @param {number} start The start of the view. - * @param {number} end The end of the view. - * @param {Array} transforms The transformations to apply to the view. - * @returns {Object} Returns an object containing the `start` and `end` - * positions of the view. - */ - function getView(start, end, transforms) { - var index = -1, - length = transforms.length; - - while (++index < length) { - var data = transforms[index], - size = data.size; - - switch (data.type) { - case 'drop': start += size; break; - case 'dropRight': end -= size; break; - case 'take': end = nativeMin(end, start + size); break; - case 'takeRight': start = nativeMax(start, end - size); break; - } - } - return { 'start': start, 'end': end }; - } - - /** - * Initializes an array clone. - * - * @private - * @param {Array} array The array to clone. - * @returns {Array} Returns the initialized clone. - */ - function initCloneArray(array) { - var length = array.length, - result = new array.constructor(length); - - // Add array properties assigned by `RegExp#exec`. - if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { - result.index = array.index; - result.input = array.input; - } - return result; - } - - /** - * Initializes an object clone. - * - * @private - * @param {Object} object The object to clone. - * @returns {Object} Returns the initialized clone. - */ - function initCloneObject(object) { - var Ctor = object.constructor; - if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) { - Ctor = Object; - } - return new Ctor; - } - - /** - * Initializes an object clone based on its `toStringTag`. - * - * **Note:** This function only supports cloning values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to clone. - * @param {string} tag The `toStringTag` of the object to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the initialized clone. - */ - function initCloneByTag(object, tag, isDeep) { - var Ctor = object.constructor; - switch (tag) { - case arrayBufferTag: - return bufferClone(object); - - case boolTag: - case dateTag: - return new Ctor(+object); - - case float32Tag: case float64Tag: - case int8Tag: case int16Tag: case int32Tag: - case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: - var buffer = object.buffer; - return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length); - - case numberTag: - case stringTag: - return new Ctor(object); - - case regexpTag: - var result = new Ctor(object.source, reFlags.exec(object)); - result.lastIndex = object.lastIndex; - } - return result; - } - - /** - * Invokes the method at `path` on `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the method to invoke. - * @param {Array} args The arguments to invoke the method with. - * @returns {*} Returns the result of the invoked method. - */ - function invokePath(object, path, args) { - if (object != null && !isKey(path, object)) { - path = toPath(path); - object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); - path = last(path); - } - var func = object == null ? object : object[path]; - return func == null ? undefined : func.apply(object, args); - } - - /** - * Checks if `value` is array-like. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. - */ - function isArrayLike(value) { - return value != null && isLength(getLength(value)); - } - - /** - * Checks if `value` is a valid array-like index. - * - * @private - * @param {*} value The value to check. - * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. - * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. - */ - function isIndex(value, length) { - value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1; - length = length == null ? MAX_SAFE_INTEGER : length; - return value > -1 && value % 1 == 0 && value < length; - } - - /** - * Checks if the provided arguments are from an iteratee call. - * - * @private - * @param {*} value The potential iteratee value argument. - * @param {*} index The potential iteratee index or key argument. - * @param {*} object The potential iteratee object argument. - * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. - */ - function isIterateeCall(value, index, object) { - if (!isObject(object)) { - return false; - } - var type = typeof index; - if (type == 'number' - ? (isArrayLike(object) && isIndex(index, object.length)) - : (type == 'string' && index in object)) { - var other = object[index]; - return value === value ? (value === other) : (other !== other); - } - return false; - } - - /** - * Checks if `value` is a property name and not a property path. - * - * @private - * @param {*} value The value to check. - * @param {Object} [object] The object to query keys on. - * @returns {boolean} Returns `true` if `value` is a property name, else `false`. - */ - function isKey(value, object) { - var type = typeof value; - if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') { - return true; - } - if (isArray(value)) { - return false; - } - var result = !reIsDeepProp.test(value); - return result || (object != null && value in toObject(object)); - } - - /** - * Checks if `func` has a lazy counterpart. - * - * @private - * @param {Function} func The function to check. - * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`. - */ - function isLaziable(func) { - var funcName = getFuncName(func); - if (!(funcName in LazyWrapper.prototype)) { - return false; - } - var other = lodash[funcName]; - if (func === other) { - return true; - } - var data = getData(other); - return !!data && func === data[0]; - } - - /** - * Checks if `value` is a valid array-like length. - * - * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - */ - function isLength(value) { - return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; - } - - /** - * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` if suitable for strict - * equality comparisons, else `false`. - */ - function isStrictComparable(value) { - return value === value && !isObject(value); - } - - /** - * Merges the function metadata of `source` into `data`. - * - * Merging metadata reduces the number of wrappers required to invoke a function. - * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` - * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg` - * augment function arguments, making the order in which they are executed important, - * preventing the merging of metadata. However, we make an exception for a safe - * common case where curried functions have `_.ary` and or `_.rearg` applied. - * - * @private - * @param {Array} data The destination metadata. - * @param {Array} source The source metadata. - * @returns {Array} Returns `data`. - */ - function mergeData(data, source) { - var bitmask = data[1], - srcBitmask = source[1], - newBitmask = bitmask | srcBitmask, - isCommon = newBitmask < ARY_FLAG; - - var isCombo = - (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) || - (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) || - (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG); - - // Exit early if metadata can't be merged. - if (!(isCommon || isCombo)) { - return data; - } - // Use source `thisArg` if available. - if (srcBitmask & BIND_FLAG) { - data[2] = source[2]; - // Set when currying a bound function. - newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG; - } - // Compose partial arguments. - var value = source[3]; - if (value) { - var partials = data[3]; - data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value); - data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]); - } - // Compose partial right arguments. - value = source[5]; - if (value) { - partials = data[5]; - data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value); - data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]); - } - // Use source `argPos` if available. - value = source[7]; - if (value) { - data[7] = arrayCopy(value); - } - // Use source `ary` if it's smaller. - if (srcBitmask & ARY_FLAG) { - data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); - } - // Use source `arity` if one is not provided. - if (data[9] == null) { - data[9] = source[9]; - } - // Use source `func` and merge bitmasks. - data[0] = source[0]; - data[1] = newBitmask; - - return data; - } - - /** - * Used by `_.defaultsDeep` to customize its `_.merge` use. - * - * @private - * @param {*} objectValue The destination object property value. - * @param {*} sourceValue The source object property value. - * @returns {*} Returns the value to assign to the destination object. - */ - function mergeDefaults(objectValue, sourceValue) { - return objectValue === undefined ? sourceValue : merge(objectValue, sourceValue, mergeDefaults); - } - - /** - * A specialized version of `_.pick` which picks `object` properties specified - * by `props`. - * - * @private - * @param {Object} object The source object. - * @param {string[]} props The property names to pick. - * @returns {Object} Returns the new object. - */ - function pickByArray(object, props) { - object = toObject(object); - - var index = -1, - length = props.length, - result = {}; - - while (++index < length) { - var key = props[index]; - if (key in object) { - result[key] = object[key]; - } - } - return result; - } - - /** - * A specialized version of `_.pick` which picks `object` properties `predicate` - * returns truthy for. - * - * @private - * @param {Object} object The source object. - * @param {Function} predicate The function invoked per iteration. - * @returns {Object} Returns the new object. - */ - function pickByCallback(object, predicate) { - var result = {}; - baseForIn(object, function(value, key, object) { - if (predicate(value, key, object)) { - result[key] = value; - } - }); - return result; - } - - /** - * Reorder `array` according to the specified indexes where the element at - * the first index is assigned as the first element, the element at - * the second index is assigned as the second element, and so on. - * - * @private - * @param {Array} array The array to reorder. - * @param {Array} indexes The arranged array indexes. - * @returns {Array} Returns `array`. - */ - function reorder(array, indexes) { - var arrLength = array.length, - length = nativeMin(indexes.length, arrLength), - oldArray = arrayCopy(array); - - while (length--) { - var index = indexes[length]; - array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; - } - return array; - } - - /** - * Sets metadata for `func`. - * - * **Note:** If this function becomes hot, i.e. is invoked a lot in a short - * period of time, it will trip its breaker and transition to an identity function - * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070) - * for more details. - * - * @private - * @param {Function} func The function to associate metadata with. - * @param {*} data The metadata. - * @returns {Function} Returns `func`. - */ - var setData = (function() { - var count = 0, - lastCalled = 0; - - return function(key, value) { - var stamp = now(), - remaining = HOT_SPAN - (stamp - lastCalled); - - lastCalled = stamp; - if (remaining > 0) { - if (++count >= HOT_COUNT) { - return key; - } - } else { - count = 0; - } - return baseSetData(key, value); - }; - }()); - - /** - * A fallback implementation of `Object.keys` which creates an array of the - * own enumerable property names of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function shimKeys(object) { - var props = keysIn(object), - propsLength = props.length, - length = propsLength && object.length; - - var allowIndexes = !!length && isLength(length) && - (isArray(object) || isArguments(object)); - - var index = -1, - result = []; - - while (++index < propsLength) { - var key = props[index]; - if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { - result.push(key); - } - } - return result; - } - - /** - * Converts `value` to an array-like object if it's not one. - * - * @private - * @param {*} value The value to process. - * @returns {Array|Object} Returns the array-like object. - */ - function toIterable(value) { - if (value == null) { - return []; - } - if (!isArrayLike(value)) { - return values(value); - } - return isObject(value) ? value : Object(value); - } - - /** - * Converts `value` to an object if it's not one. - * - * @private - * @param {*} value The value to process. - * @returns {Object} Returns the object. - */ - function toObject(value) { - return isObject(value) ? value : Object(value); - } - - /** - * Converts `value` to property path array if it's not one. - * - * @private - * @param {*} value The value to process. - * @returns {Array} Returns the property path array. - */ - function toPath(value) { - if (isArray(value)) { - return value; - } - var result = []; - baseToString(value).replace(rePropName, function(match, number, quote, string) { - result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); - }); - return result; - } - - /** - * Creates a clone of `wrapper`. - * - * @private - * @param {Object} wrapper The wrapper to clone. - * @returns {Object} Returns the cloned wrapper. - */ - function wrapperClone(wrapper) { - return wrapper instanceof LazyWrapper - ? wrapper.clone() - : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates an array of elements split into groups the length of `size`. - * If `collection` can't be split evenly, the final chunk will be the remaining - * elements. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to process. - * @param {number} [size=1] The length of each chunk. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the new array containing chunks. - * @example - * - * _.chunk(['a', 'b', 'c', 'd'], 2); - * // => [['a', 'b'], ['c', 'd']] - * - * _.chunk(['a', 'b', 'c', 'd'], 3); - * // => [['a', 'b', 'c'], ['d']] - */ - function chunk(array, size, guard) { - if (guard ? isIterateeCall(array, size, guard) : size == null) { - size = 1; - } else { - size = nativeMax(nativeFloor(size) || 1, 1); - } - var index = 0, - length = array ? array.length : 0, - resIndex = -1, - result = Array(nativeCeil(length / size)); - - while (index < length) { - result[++resIndex] = baseSlice(array, index, (index += size)); - } - return result; - } - - /** - * Creates an array with all falsey values removed. The values `false`, `null`, - * `0`, `""`, `undefined`, and `NaN` are falsey. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to compact. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.compact([0, 1, false, 2, '', 3]); - * // => [1, 2, 3] - */ - function compact(array) { - var index = -1, - length = array ? array.length : 0, - resIndex = -1, - result = []; - - while (++index < length) { - var value = array[index]; - if (value) { - result[++resIndex] = value; - } - } - return result; - } - - /** - * Creates an array of unique `array` values not included in the other - * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The arrays of values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.difference([1, 2, 3], [4, 2]); - * // => [1, 3] - */ - var difference = restParam(function(array, values) { - return (isObjectLike(array) && isArrayLike(array)) - ? baseDifference(array, baseFlatten(values, false, true)) - : []; - }); - - /** - * Creates a slice of `array` with `n` elements dropped from the beginning. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.drop([1, 2, 3]); - * // => [2, 3] - * - * _.drop([1, 2, 3], 2); - * // => [3] - * - * _.drop([1, 2, 3], 5); - * // => [] - * - * _.drop([1, 2, 3], 0); - * // => [1, 2, 3] - */ - function drop(array, n, guard) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (guard ? isIterateeCall(array, n, guard) : n == null) { - n = 1; - } - return baseSlice(array, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` with `n` elements dropped from the end. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropRight([1, 2, 3]); - * // => [1, 2] - * - * _.dropRight([1, 2, 3], 2); - * // => [1] - * - * _.dropRight([1, 2, 3], 5); - * // => [] - * - * _.dropRight([1, 2, 3], 0); - * // => [1, 2, 3] - */ - function dropRight(array, n, guard) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (guard ? isIterateeCall(array, n, guard) : n == null) { - n = 1; - } - n = length - (+n || 0); - return baseSlice(array, 0, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` excluding elements dropped from the end. - * Elements are dropped until `predicate` returns falsey. The predicate is - * bound to `thisArg` and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that match the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropRightWhile([1, 2, 3], function(n) { - * return n > 1; - * }); - * // => [1] - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.dropRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); - * // => ['barney', 'fred'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.dropRightWhile(users, 'active', false), 'user'); - * // => ['barney'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.dropRightWhile(users, 'active'), 'user'); - * // => ['barney', 'fred', 'pebbles'] - */ - function dropRightWhile(array, predicate, thisArg) { - return (array && array.length) - ? baseWhile(array, getCallback(predicate, thisArg, 3), true, true) - : []; - } - - /** - * Creates a slice of `array` excluding elements dropped from the beginning. - * Elements are dropped until `predicate` returns falsey. The predicate is - * bound to `thisArg` and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.dropWhile([1, 2, 3], function(n) { - * return n < 3; - * }); - * // => [3] - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.dropWhile(users, { 'user': 'barney', 'active': false }), 'user'); - * // => ['fred', 'pebbles'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.dropWhile(users, 'active', false), 'user'); - * // => ['pebbles'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.dropWhile(users, 'active'), 'user'); - * // => ['barney', 'fred', 'pebbles'] - */ - function dropWhile(array, predicate, thisArg) { - return (array && array.length) - ? baseWhile(array, getCallback(predicate, thisArg, 3), true) - : []; - } - - /** - * Fills elements of `array` with `value` from `start` up to, but not - * including, `end`. - * - * **Note:** This method mutates `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to fill. - * @param {*} value The value to fill `array` with. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3]; - * - * _.fill(array, 'a'); - * console.log(array); - * // => ['a', 'a', 'a'] - * - * _.fill(Array(3), 2); - * // => [2, 2, 2] - * - * _.fill([4, 6, 8], '*', 1, 2); - * // => [4, '*', 8] - */ - function fill(array, value, start, end) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { - start = 0; - end = length; - } - return baseFill(array, value, start, end); - } - - /** - * This method is like `_.find` except that it returns the index of the first - * element `predicate` returns truthy for instead of the element itself. - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * _.findIndex(users, function(chr) { - * return chr.user == 'barney'; - * }); - * // => 0 - * - * // using the `_.matches` callback shorthand - * _.findIndex(users, { 'user': 'fred', 'active': false }); - * // => 1 - * - * // using the `_.matchesProperty` callback shorthand - * _.findIndex(users, 'active', false); - * // => 0 - * - * // using the `_.property` callback shorthand - * _.findIndex(users, 'active'); - * // => 2 - */ - var findIndex = createFindIndex(); - - /** - * This method is like `_.findIndex` except that it iterates over elements - * of `collection` from right to left. - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * _.findLastIndex(users, function(chr) { - * return chr.user == 'pebbles'; - * }); - * // => 2 - * - * // using the `_.matches` callback shorthand - * _.findLastIndex(users, { 'user': 'barney', 'active': true }); - * // => 0 - * - * // using the `_.matchesProperty` callback shorthand - * _.findLastIndex(users, 'active', false); - * // => 2 - * - * // using the `_.property` callback shorthand - * _.findLastIndex(users, 'active'); - * // => 0 - */ - var findLastIndex = createFindIndex(true); - - /** - * Gets the first element of `array`. - * - * @static - * @memberOf _ - * @alias head - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the first element of `array`. - * @example - * - * _.first([1, 2, 3]); - * // => 1 - * - * _.first([]); - * // => undefined - */ - function first(array) { - return array ? array[0] : undefined; - } - - /** - * Flattens a nested array. If `isDeep` is `true` the array is recursively - * flattened, otherwise it is only flattened a single level. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to flatten. - * @param {boolean} [isDeep] Specify a deep flatten. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the new flattened array. - * @example - * - * _.flatten([1, [2, 3, [4]]]); - * // => [1, 2, 3, [4]] - * - * // using `isDeep` - * _.flatten([1, [2, 3, [4]]], true); - * // => [1, 2, 3, 4] - */ - function flatten(array, isDeep, guard) { - var length = array ? array.length : 0; - if (guard && isIterateeCall(array, isDeep, guard)) { - isDeep = false; - } - return length ? baseFlatten(array, isDeep) : []; - } - - /** - * Recursively flattens a nested array. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to recursively flatten. - * @returns {Array} Returns the new flattened array. - * @example - * - * _.flattenDeep([1, [2, 3, [4]]]); - * // => [1, 2, 3, 4] - */ - function flattenDeep(array) { - var length = array ? array.length : 0; - return length ? baseFlatten(array, true) : []; - } - - /** - * Gets the index at which the first occurrence of `value` is found in `array` - * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. If `fromIndex` is negative, it is used as the offset - * from the end of `array`. If `array` is sorted providing `true` for `fromIndex` - * performs a faster binary search. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {boolean|number} [fromIndex=0] The index to search from or `true` - * to perform a binary search on a sorted array. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.indexOf([1, 2, 1, 2], 2); - * // => 1 - * - * // using `fromIndex` - * _.indexOf([1, 2, 1, 2], 2, 2); - * // => 3 - * - * // performing a binary search - * _.indexOf([1, 1, 2, 2], 2, true); - * // => 2 - */ - function indexOf(array, value, fromIndex) { - var length = array ? array.length : 0; - if (!length) { - return -1; - } - if (typeof fromIndex == 'number') { - fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; - } else if (fromIndex) { - var index = binaryIndex(array, value); - if (index < length && - (value === value ? (value === array[index]) : (array[index] !== array[index]))) { - return index; - } - return -1; - } - return baseIndexOf(array, value, fromIndex || 0); - } - - /** - * Gets all but the last element of `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.initial([1, 2, 3]); - * // => [1, 2] - */ - function initial(array) { - return dropRight(array, 1); - } - - /** - * Creates an array of unique values that are included in all of the provided - * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of shared values. - * @example - * _.intersection([1, 2], [4, 2], [2, 1]); - * // => [2] - */ - var intersection = restParam(function(arrays) { - var othLength = arrays.length, - othIndex = othLength, - caches = Array(length), - indexOf = getIndexOf(), - isCommon = indexOf == baseIndexOf, - result = []; - - while (othIndex--) { - var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : []; - caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null; - } - var array = arrays[0], - index = -1, - length = array ? array.length : 0, - seen = caches[0]; - - outer: - while (++index < length) { - value = array[index]; - if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) { - var othIndex = othLength; - while (--othIndex) { - var cache = caches[othIndex]; - if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) { - continue outer; - } - } - if (seen) { - seen.push(value); - } - result.push(value); - } - } - return result; - }); - - /** - * Gets the last element of `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the last element of `array`. - * @example - * - * _.last([1, 2, 3]); - * // => 3 - */ - function last(array) { - var length = array ? array.length : 0; - return length ? array[length - 1] : undefined; - } - - /** - * This method is like `_.indexOf` except that it iterates over elements of - * `array` from right to left. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {boolean|number} [fromIndex=array.length-1] The index to search from - * or `true` to perform a binary search on a sorted array. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * _.lastIndexOf([1, 2, 1, 2], 2); - * // => 3 - * - * // using `fromIndex` - * _.lastIndexOf([1, 2, 1, 2], 2, 2); - * // => 1 - * - * // performing a binary search - * _.lastIndexOf([1, 1, 2, 2], 2, true); - * // => 3 - */ - function lastIndexOf(array, value, fromIndex) { - var length = array ? array.length : 0; - if (!length) { - return -1; - } - var index = length; - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1; - } else if (fromIndex) { - index = binaryIndex(array, value, true) - 1; - var other = array[index]; - if (value === value ? (value === other) : (other !== other)) { - return index; - } - return -1; - } - if (value !== value) { - return indexOfNaN(array, index, true); - } - while (index--) { - if (array[index] === value) { - return index; - } - } - return -1; - } - - /** - * Removes all provided values from `array` using - * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * **Note:** Unlike `_.without`, this method mutates `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to modify. - * @param {...*} [values] The values to remove. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3, 1, 2, 3]; - * - * _.pull(array, 2, 3); - * console.log(array); - * // => [1, 1] - */ - function pull() { - var args = arguments, - array = args[0]; - - if (!(array && array.length)) { - return array; - } - var index = 0, - indexOf = getIndexOf(), - length = args.length; - - while (++index < length) { - var fromIndex = 0, - value = args[index]; - - while ((fromIndex = indexOf(array, value, fromIndex)) > -1) { - splice.call(array, fromIndex, 1); - } - } - return array; - } - - /** - * Removes elements from `array` corresponding to the given indexes and returns - * an array of the removed elements. Indexes may be specified as an array of - * indexes or as individual arguments. - * - * **Note:** Unlike `_.at`, this method mutates `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to modify. - * @param {...(number|number[])} [indexes] The indexes of elements to remove, - * specified as individual indexes or arrays of indexes. - * @returns {Array} Returns the new array of removed elements. - * @example - * - * var array = [5, 10, 15, 20]; - * var evens = _.pullAt(array, 1, 3); - * - * console.log(array); - * // => [5, 15] - * - * console.log(evens); - * // => [10, 20] - */ - var pullAt = restParam(function(array, indexes) { - indexes = baseFlatten(indexes); - - var result = baseAt(array, indexes); - basePullAt(array, indexes.sort(baseCompareAscending)); - return result; - }); - - /** - * Removes all elements from `array` that `predicate` returns truthy for - * and returns an array of the removed elements. The predicate is bound to - * `thisArg` and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * **Note:** Unlike `_.filter`, this method mutates `array`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to modify. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the new array of removed elements. - * @example - * - * var array = [1, 2, 3, 4]; - * var evens = _.remove(array, function(n) { - * return n % 2 == 0; - * }); - * - * console.log(array); - * // => [1, 3] - * - * console.log(evens); - * // => [2, 4] - */ - function remove(array, predicate, thisArg) { - var result = []; - if (!(array && array.length)) { - return result; - } - var index = -1, - indexes = [], - length = array.length; - - predicate = getCallback(predicate, thisArg, 3); - while (++index < length) { - var value = array[index]; - if (predicate(value, index, array)) { - result.push(value); - indexes.push(index); - } - } - basePullAt(array, indexes); - return result; - } - - /** - * Gets all but the first element of `array`. - * - * @static - * @memberOf _ - * @alias tail - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.rest([1, 2, 3]); - * // => [2, 3] - */ - function rest(array) { - return drop(array, 1); - } - - /** - * Creates a slice of `array` from `start` up to, but not including, `end`. - * - * **Note:** This method is used instead of `Array#slice` to support node - * lists in IE < 9 and to ensure dense arrays are returned. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. - * @param {number} [end=array.length] The end position. - * @returns {Array} Returns the slice of `array`. - */ - function slice(array, start, end) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { - start = 0; - end = length; - } - return baseSlice(array, start, end); - } - - /** - * Uses a binary search to determine the lowest index at which `value` should - * be inserted into `array` in order to maintain its sort order. If an iteratee - * function is provided it is invoked for `value` and each element of `array` - * to compute their sort ranking. The iteratee is bound to `thisArg` and - * invoked with one argument; (value). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedIndex([30, 50], 40); - * // => 1 - * - * _.sortedIndex([4, 4, 5, 5], 5); - * // => 2 - * - * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } }; - * - * // using an iteratee function - * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) { - * return this.data[word]; - * }, dict); - * // => 1 - * - * // using the `_.property` callback shorthand - * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); - * // => 1 - */ - var sortedIndex = createSortedIndex(); - - /** - * This method is like `_.sortedIndex` except that it returns the highest - * index at which `value` should be inserted into `array` in order to - * maintain its sort order. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedLastIndex([4, 4, 5, 5], 5); - * // => 4 - */ - var sortedLastIndex = createSortedIndex(true); - - /** - * Creates a slice of `array` with `n` elements taken from the beginning. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.take([1, 2, 3]); - * // => [1] - * - * _.take([1, 2, 3], 2); - * // => [1, 2] - * - * _.take([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.take([1, 2, 3], 0); - * // => [] - */ - function take(array, n, guard) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (guard ? isIterateeCall(array, n, guard) : n == null) { - n = 1; - } - return baseSlice(array, 0, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` with `n` elements taken from the end. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeRight([1, 2, 3]); - * // => [3] - * - * _.takeRight([1, 2, 3], 2); - * // => [2, 3] - * - * _.takeRight([1, 2, 3], 5); - * // => [1, 2, 3] - * - * _.takeRight([1, 2, 3], 0); - * // => [] - */ - function takeRight(array, n, guard) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (guard ? isIterateeCall(array, n, guard) : n == null) { - n = 1; - } - n = length - (+n || 0); - return baseSlice(array, n < 0 ? 0 : n); - } - - /** - * Creates a slice of `array` with elements taken from the end. Elements are - * taken until `predicate` returns falsey. The predicate is bound to `thisArg` - * and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeRightWhile([1, 2, 3], function(n) { - * return n > 1; - * }); - * // => [2, 3] - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.takeRightWhile(users, { 'user': 'pebbles', 'active': false }), 'user'); - * // => ['pebbles'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.takeRightWhile(users, 'active', false), 'user'); - * // => ['fred', 'pebbles'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.takeRightWhile(users, 'active'), 'user'); - * // => [] - */ - function takeRightWhile(array, predicate, thisArg) { - return (array && array.length) - ? baseWhile(array, getCallback(predicate, thisArg, 3), false, true) - : []; - } - - /** - * Creates a slice of `array` with elements taken from the beginning. Elements - * are taken until `predicate` returns falsey. The predicate is bound to - * `thisArg` and invoked with three arguments: (value, index, array). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to query. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the slice of `array`. - * @example - * - * _.takeWhile([1, 2, 3], function(n) { - * return n < 3; - * }); - * // => [1, 2] - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false}, - * { 'user': 'pebbles', 'active': true } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.takeWhile(users, { 'user': 'barney', 'active': false }), 'user'); - * // => ['barney'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.takeWhile(users, 'active', false), 'user'); - * // => ['barney', 'fred'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.takeWhile(users, 'active'), 'user'); - * // => [] - */ - function takeWhile(array, predicate, thisArg) { - return (array && array.length) - ? baseWhile(array, getCallback(predicate, thisArg, 3)) - : []; - } - - /** - * Creates an array of unique values, in order, from all of the provided arrays - * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of combined values. - * @example - * - * _.union([1, 2], [4, 2], [2, 1]); - * // => [1, 2, 4] - */ - var union = restParam(function(arrays) { - return baseUniq(baseFlatten(arrays, false, true)); - }); - - /** - * Creates a duplicate-free version of an array, using - * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons, in which only the first occurence of each element - * is kept. Providing `true` for `isSorted` performs a faster search algorithm - * for sorted arrays. If an iteratee function is provided it is invoked for - * each element in the array to generate the criterion by which uniqueness - * is computed. The `iteratee` is bound to `thisArg` and invoked with three - * arguments: (value, index, array). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias unique - * @category Array - * @param {Array} array The array to inspect. - * @param {boolean} [isSorted] Specify the array is sorted. - * @param {Function|Object|string} [iteratee] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new duplicate-value-free array. - * @example - * - * _.uniq([2, 1, 2]); - * // => [2, 1] - * - * // using `isSorted` - * _.uniq([1, 1, 2], true); - * // => [1, 2] - * - * // using an iteratee function - * _.uniq([1, 2.5, 1.5, 2], function(n) { - * return this.floor(n); - * }, Math); - * // => [1, 2.5] - * - * // using the `_.property` callback shorthand - * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); - * // => [{ 'x': 1 }, { 'x': 2 }] - */ - function uniq(array, isSorted, iteratee, thisArg) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - if (isSorted != null && typeof isSorted != 'boolean') { - thisArg = iteratee; - iteratee = isIterateeCall(array, isSorted, thisArg) ? undefined : isSorted; - isSorted = false; - } - var callback = getCallback(); - if (!(iteratee == null && callback === baseCallback)) { - iteratee = callback(iteratee, thisArg, 3); - } - return (isSorted && getIndexOf() == baseIndexOf) - ? sortedUniq(array, iteratee) - : baseUniq(array, iteratee); - } - - /** - * This method is like `_.zip` except that it accepts an array of grouped - * elements and creates an array regrouping the elements to their pre-zip - * configuration. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array of grouped elements to process. - * @returns {Array} Returns the new array of regrouped elements. - * @example - * - * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); - * // => [['fred', 30, true], ['barney', 40, false]] - * - * _.unzip(zipped); - * // => [['fred', 'barney'], [30, 40], [true, false]] - */ - function unzip(array) { - if (!(array && array.length)) { - return []; - } - var index = -1, - length = 0; - - array = arrayFilter(array, function(group) { - if (isArrayLike(group)) { - length = nativeMax(group.length, length); - return true; - } - }); - var result = Array(length); - while (++index < length) { - result[index] = arrayMap(array, baseProperty(index)); - } - return result; - } - - /** - * This method is like `_.unzip` except that it accepts an iteratee to specify - * how regrouped values should be combined. The `iteratee` is bound to `thisArg` - * and invoked with four arguments: (accumulator, value, index, group). - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array of grouped elements to process. - * @param {Function} [iteratee] The function to combine regrouped values. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new array of regrouped elements. - * @example - * - * var zipped = _.zip([1, 2], [10, 20], [100, 200]); - * // => [[1, 10, 100], [2, 20, 200]] - * - * _.unzipWith(zipped, _.add); - * // => [3, 30, 300] - */ - function unzipWith(array, iteratee, thisArg) { - var length = array ? array.length : 0; - if (!length) { - return []; - } - var result = unzip(array); - if (iteratee == null) { - return result; - } - iteratee = bindCallback(iteratee, thisArg, 4); - return arrayMap(result, function(group) { - return arrayReduce(group, iteratee, undefined, true); - }); - } - - /** - * Creates an array excluding all provided values using - * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. - * - * @static - * @memberOf _ - * @category Array - * @param {Array} array The array to filter. - * @param {...*} [values] The values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * _.without([1, 2, 1, 3], 1, 2); - * // => [3] - */ - var without = restParam(function(array, values) { - return isArrayLike(array) - ? baseDifference(array, values) - : []; - }); - - /** - * Creates an array of unique values that is the [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) - * of the provided arrays. - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of values. - * @example - * - * _.xor([1, 2], [4, 2]); - * // => [1, 4] - */ - function xor() { - var index = -1, - length = arguments.length; - - while (++index < length) { - var array = arguments[index]; - if (isArrayLike(array)) { - var result = result - ? arrayPush(baseDifference(result, array), baseDifference(array, result)) - : array; - } - } - return result ? baseUniq(result) : []; - } - - /** - * Creates an array of grouped elements, the first of which contains the first - * elements of the given arrays, the second of which contains the second elements - * of the given arrays, and so on. - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to process. - * @returns {Array} Returns the new array of grouped elements. - * @example - * - * _.zip(['fred', 'barney'], [30, 40], [true, false]); - * // => [['fred', 30, true], ['barney', 40, false]] - */ - var zip = restParam(unzip); - - /** - * The inverse of `_.pairs`; this method returns an object composed from arrays - * of property names and values. Provide either a single two dimensional array, - * e.g. `[[key1, value1], [key2, value2]]` or two arrays, one of property names - * and one of corresponding values. - * - * @static - * @memberOf _ - * @alias object - * @category Array - * @param {Array} props The property names. - * @param {Array} [values=[]] The property values. - * @returns {Object} Returns the new object. - * @example - * - * _.zipObject([['fred', 30], ['barney', 40]]); - * // => { 'fred': 30, 'barney': 40 } - * - * _.zipObject(['fred', 'barney'], [30, 40]); - * // => { 'fred': 30, 'barney': 40 } - */ - function zipObject(props, values) { - var index = -1, - length = props ? props.length : 0, - result = {}; - - if (length && !values && !isArray(props[0])) { - values = []; - } - while (++index < length) { - var key = props[index]; - if (values) { - result[key] = values[index]; - } else if (key) { - result[key[0]] = key[1]; - } - } - return result; - } - - /** - * This method is like `_.zip` except that it accepts an iteratee to specify - * how grouped values should be combined. The `iteratee` is bound to `thisArg` - * and invoked with four arguments: (accumulator, value, index, group). - * - * @static - * @memberOf _ - * @category Array - * @param {...Array} [arrays] The arrays to process. - * @param {Function} [iteratee] The function to combine grouped values. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new array of grouped elements. - * @example - * - * _.zipWith([1, 2], [10, 20], [100, 200], _.add); - * // => [111, 222] - */ - var zipWith = restParam(function(arrays) { - var length = arrays.length, - iteratee = length > 2 ? arrays[length - 2] : undefined, - thisArg = length > 1 ? arrays[length - 1] : undefined; - - if (length > 2 && typeof iteratee == 'function') { - length -= 2; - } else { - iteratee = (length > 1 && typeof thisArg == 'function') ? (--length, thisArg) : undefined; - thisArg = undefined; - } - arrays.length = length; - return unzipWith(arrays, iteratee, thisArg); - }); - - /*------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` object that wraps `value` with explicit method - * chaining enabled. - * - * @static - * @memberOf _ - * @category Chain - * @param {*} value The value to wrap. - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'pebbles', 'age': 1 } - * ]; - * - * var youngest = _.chain(users) - * .sortBy('age') - * .map(function(chr) { - * return chr.user + ' is ' + chr.age; - * }) - * .first() - * .value(); - * // => 'pebbles is 1' - */ - function chain(value) { - var result = lodash(value); - result.__chain__ = true; - return result; - } - - /** - * This method invokes `interceptor` and returns `value`. The interceptor is - * bound to `thisArg` and invoked with one argument; (value). The purpose of - * this method is to "tap into" a method chain in order to perform operations - * on intermediate results within the chain. - * - * @static - * @memberOf _ - * @category Chain - * @param {*} value The value to provide to `interceptor`. - * @param {Function} interceptor The function to invoke. - * @param {*} [thisArg] The `this` binding of `interceptor`. - * @returns {*} Returns `value`. - * @example - * - * _([1, 2, 3]) - * .tap(function(array) { - * array.pop(); - * }) - * .reverse() - * .value(); - * // => [2, 1] - */ - function tap(value, interceptor, thisArg) { - interceptor.call(thisArg, value); - return value; - } - - /** - * This method is like `_.tap` except that it returns the result of `interceptor`. - * - * @static - * @memberOf _ - * @category Chain - * @param {*} value The value to provide to `interceptor`. - * @param {Function} interceptor The function to invoke. - * @param {*} [thisArg] The `this` binding of `interceptor`. - * @returns {*} Returns the result of `interceptor`. - * @example - * - * _(' abc ') - * .chain() - * .trim() - * .thru(function(value) { - * return [value]; - * }) - * .value(); - * // => ['abc'] - */ - function thru(value, interceptor, thisArg) { - return interceptor.call(thisArg, value); - } - - /** - * Enables explicit method chaining on the wrapper object. - * - * @name chain - * @memberOf _ - * @category Chain - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 } - * ]; - * - * // without explicit chaining - * _(users).first(); - * // => { 'user': 'barney', 'age': 36 } - * - * // with explicit chaining - * _(users).chain() - * .first() - * .pick('user') - * .value(); - * // => { 'user': 'barney' } - */ - function wrapperChain() { - return chain(this); - } - - /** - * Executes the chained sequence and returns the wrapped result. - * - * @name commit - * @memberOf _ - * @category Chain - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var array = [1, 2]; - * var wrapped = _(array).push(3); - * - * console.log(array); - * // => [1, 2] - * - * wrapped = wrapped.commit(); - * console.log(array); - * // => [1, 2, 3] - * - * wrapped.last(); - * // => 3 - * - * console.log(array); - * // => [1, 2, 3] - */ - function wrapperCommit() { - return new LodashWrapper(this.value(), this.__chain__); - } - - /** - * Creates a new array joining a wrapped array with any additional arrays - * and/or values. - * - * @name concat - * @memberOf _ - * @category Chain - * @param {...*} [values] The values to concatenate. - * @returns {Array} Returns the new concatenated array. - * @example - * - * var array = [1]; - * var wrapped = _(array).concat(2, [3], [[4]]); - * - * console.log(wrapped.value()); - * // => [1, 2, 3, [4]] - * - * console.log(array); - * // => [1] - */ - var wrapperConcat = restParam(function(values) { - values = baseFlatten(values); - return this.thru(function(array) { - return arrayConcat(isArray(array) ? array : [toObject(array)], values); - }); - }); - - /** - * Creates a clone of the chained sequence planting `value` as the wrapped value. - * - * @name plant - * @memberOf _ - * @category Chain - * @returns {Object} Returns the new `lodash` wrapper instance. - * @example - * - * var array = [1, 2]; - * var wrapped = _(array).map(function(value) { - * return Math.pow(value, 2); - * }); - * - * var other = [3, 4]; - * var otherWrapped = wrapped.plant(other); - * - * otherWrapped.value(); - * // => [9, 16] - * - * wrapped.value(); - * // => [1, 4] - */ - function wrapperPlant(value) { - var result, - parent = this; - - while (parent instanceof baseLodash) { - var clone = wrapperClone(parent); - if (result) { - previous.__wrapped__ = clone; - } else { - result = clone; - } - var previous = clone; - parent = parent.__wrapped__; - } - previous.__wrapped__ = value; - return result; - } - - /** - * Reverses the wrapped array so the first element becomes the last, the - * second element becomes the second to last, and so on. - * - * **Note:** This method mutates the wrapped array. - * - * @name reverse - * @memberOf _ - * @category Chain - * @returns {Object} Returns the new reversed `lodash` wrapper instance. - * @example - * - * var array = [1, 2, 3]; - * - * _(array).reverse().value() - * // => [3, 2, 1] - * - * console.log(array); - * // => [3, 2, 1] - */ - function wrapperReverse() { - var value = this.__wrapped__; - - var interceptor = function(value) { - return (wrapped && wrapped.__dir__ < 0) ? value : value.reverse(); - }; - if (value instanceof LazyWrapper) { - var wrapped = value; - if (this.__actions__.length) { - wrapped = new LazyWrapper(this); - } - wrapped = wrapped.reverse(); - wrapped.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined }); - return new LodashWrapper(wrapped, this.__chain__); - } - return this.thru(interceptor); - } - - /** - * Produces the result of coercing the unwrapped value to a string. - * - * @name toString - * @memberOf _ - * @category Chain - * @returns {string} Returns the coerced string value. - * @example - * - * _([1, 2, 3]).toString(); - * // => '1,2,3' - */ - function wrapperToString() { - return (this.value() + ''); - } - - /** - * Executes the chained sequence to extract the unwrapped value. - * - * @name value - * @memberOf _ - * @alias run, toJSON, valueOf - * @category Chain - * @returns {*} Returns the resolved unwrapped value. - * @example - * - * _([1, 2, 3]).value(); - * // => [1, 2, 3] - */ - function wrapperValue() { - return baseWrapperValue(this.__wrapped__, this.__actions__); - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates an array of elements corresponding to the given keys, or indexes, - * of `collection`. Keys may be specified as individual arguments or as arrays - * of keys. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {...(number|number[]|string|string[])} [props] The property names - * or indexes of elements to pick, specified individually or in arrays. - * @returns {Array} Returns the new array of picked elements. - * @example - * - * _.at(['a', 'b', 'c'], [0, 2]); - * // => ['a', 'c'] - * - * _.at(['barney', 'fred', 'pebbles'], 0, 2); - * // => ['barney', 'pebbles'] - */ - var at = restParam(function(collection, props) { - return baseAt(collection, baseFlatten(props)); - }); - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` through `iteratee`. The corresponding value - * of each key is the number of times the key was returned by `iteratee`. - * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.countBy([4.3, 6.1, 6.4], function(n) { - * return Math.floor(n); - * }); - * // => { '4': 1, '6': 2 } - * - * _.countBy([4.3, 6.1, 6.4], function(n) { - * return this.floor(n); - * }, Math); - * // => { '4': 1, '6': 2 } - * - * _.countBy(['one', 'two', 'three'], 'length'); - * // => { '3': 2, '5': 1 } - */ - var countBy = createAggregator(function(result, value, key) { - hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1); - }); - - /** - * Checks if `predicate` returns truthy for **all** elements of `collection`. - * The predicate is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias all - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - * @example - * - * _.every([true, 1, null, 'yes'], Boolean); - * // => false - * - * var users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.every(users, { 'user': 'barney', 'active': false }); - * // => false - * - * // using the `_.matchesProperty` callback shorthand - * _.every(users, 'active', false); - * // => true - * - * // using the `_.property` callback shorthand - * _.every(users, 'active'); - * // => false - */ - function every(collection, predicate, thisArg) { - var func = isArray(collection) ? arrayEvery : baseEvery; - if (thisArg && isIterateeCall(collection, predicate, thisArg)) { - predicate = undefined; - } - if (typeof predicate != 'function' || thisArg !== undefined) { - predicate = getCallback(predicate, thisArg, 3); - } - return func(collection, predicate); - } - - /** - * Iterates over elements of `collection`, returning an array of all elements - * `predicate` returns truthy for. The predicate is bound to `thisArg` and - * invoked with three arguments: (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias select - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the new filtered array. - * @example - * - * _.filter([4, 5, 6], function(n) { - * return n % 2 == 0; - * }); - * // => [4, 6] - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.filter(users, { 'age': 36, 'active': true }), 'user'); - * // => ['barney'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.filter(users, 'active', false), 'user'); - * // => ['fred'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.filter(users, 'active'), 'user'); - * // => ['barney'] - */ - function filter(collection, predicate, thisArg) { - var func = isArray(collection) ? arrayFilter : baseFilter; - predicate = getCallback(predicate, thisArg, 3); - return func(collection, predicate); - } - - /** - * Iterates over elements of `collection`, returning the first element - * `predicate` returns truthy for. The predicate is bound to `thisArg` and - * invoked with three arguments: (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias detect - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false }, - * { 'user': 'pebbles', 'age': 1, 'active': true } - * ]; - * - * _.result(_.find(users, function(chr) { - * return chr.age < 40; - * }), 'user'); - * // => 'barney' - * - * // using the `_.matches` callback shorthand - * _.result(_.find(users, { 'age': 1, 'active': true }), 'user'); - * // => 'pebbles' - * - * // using the `_.matchesProperty` callback shorthand - * _.result(_.find(users, 'active', false), 'user'); - * // => 'fred' - * - * // using the `_.property` callback shorthand - * _.result(_.find(users, 'active'), 'user'); - * // => 'barney' - */ - var find = createFind(baseEach); - - /** - * This method is like `_.find` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * _.findLast([1, 2, 3, 4], function(n) { - * return n % 2 == 1; - * }); - * // => 3 - */ - var findLast = createFind(baseEachRight, true); - - /** - * Performs a deep comparison between each element in `collection` and the - * source object, returning the first element that has equivalent property - * values. - * - * **Note:** This method supports comparing arrays, booleans, `Date` objects, - * numbers, `Object` objects, regexes, and strings. Objects are compared by - * their own, not inherited, enumerable properties. For comparing a single - * own or inherited property value see `_.matchesProperty`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {Object} source The object of property values to match. - * @returns {*} Returns the matched element, else `undefined`. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': true }, - * { 'user': 'fred', 'age': 40, 'active': false } - * ]; - * - * _.result(_.findWhere(users, { 'age': 36, 'active': true }), 'user'); - * // => 'barney' - * - * _.result(_.findWhere(users, { 'age': 40, 'active': false }), 'user'); - * // => 'fred' - */ - function findWhere(collection, source) { - return find(collection, baseMatches(source)); - } - - /** - * Iterates over elements of `collection` invoking `iteratee` for each element. - * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). Iteratee functions may exit iteration early - * by explicitly returning `false`. - * - * **Note:** As with other "Collections" methods, objects with a "length" property - * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` - * may be used for object iteration. - * - * @static - * @memberOf _ - * @alias each - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array|Object|string} Returns `collection`. - * @example - * - * _([1, 2]).forEach(function(n) { - * console.log(n); - * }).value(); - * // => logs each value from left to right and returns the array - * - * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) { - * console.log(n, key); - * }); - * // => logs each value-key pair and returns the object (iteration order is not guaranteed) - */ - var forEach = createForEach(arrayEach, baseEach); - - /** - * This method is like `_.forEach` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @alias eachRight - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array|Object|string} Returns `collection`. - * @example - * - * _([1, 2]).forEachRight(function(n) { - * console.log(n); - * }).value(); - * // => logs each value from right to left and returns the array - */ - var forEachRight = createForEach(arrayEachRight, baseEachRight); - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` through `iteratee`. The corresponding value - * of each key is an array of the elements responsible for generating the key. - * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.groupBy([4.2, 6.1, 6.4], function(n) { - * return Math.floor(n); - * }); - * // => { '4': [4.2], '6': [6.1, 6.4] } - * - * _.groupBy([4.2, 6.1, 6.4], function(n) { - * return this.floor(n); - * }, Math); - * // => { '4': [4.2], '6': [6.1, 6.4] } - * - * // using the `_.property` callback shorthand - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } - */ - var groupBy = createAggregator(function(result, value, key) { - if (hasOwnProperty.call(result, key)) { - result[key].push(value); - } else { - result[key] = [value]; - } - }); - - /** - * Checks if `value` is in `collection` using - * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) - * for equality comparisons. If `fromIndex` is negative, it is used as the offset - * from the end of `collection`. - * - * @static - * @memberOf _ - * @alias contains, include - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {*} target The value to search for. - * @param {number} [fromIndex=0] The index to search from. - * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. - * @returns {boolean} Returns `true` if a matching element is found, else `false`. - * @example - * - * _.includes([1, 2, 3], 1); - * // => true - * - * _.includes([1, 2, 3], 1, 2); - * // => false - * - * _.includes({ 'user': 'fred', 'age': 40 }, 'fred'); - * // => true - * - * _.includes('pebbles', 'eb'); - * // => true - */ - function includes(collection, target, fromIndex, guard) { - var length = collection ? getLength(collection) : 0; - if (!isLength(length)) { - collection = values(collection); - length = collection.length; - } - if (typeof fromIndex != 'number' || (guard && isIterateeCall(target, fromIndex, guard))) { - fromIndex = 0; - } else { - fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); - } - return (typeof collection == 'string' || !isArray(collection) && isString(collection)) - ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1) - : (!!length && getIndexOf(collection, target, fromIndex) > -1); - } - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` through `iteratee`. The corresponding value - * of each key is the last element responsible for generating the key. The - * iteratee function is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * var keyData = [ - * { 'dir': 'left', 'code': 97 }, - * { 'dir': 'right', 'code': 100 } - * ]; - * - * _.indexBy(keyData, 'dir'); - * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } - * - * _.indexBy(keyData, function(object) { - * return String.fromCharCode(object.code); - * }); - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - * - * _.indexBy(keyData, function(object) { - * return this.fromCharCode(object.code); - * }, String); - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - */ - var indexBy = createAggregator(function(result, value, key) { - result[key] = value; - }); - - /** - * Invokes the method at `path` of each element in `collection`, returning - * an array of the results of each invoked method. Any additional arguments - * are provided to each invoked method. If `methodName` is a function it is - * invoked for, and `this` bound to, each element in `collection`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Array|Function|string} path The path of the method to invoke or - * the function invoked per iteration. - * @param {...*} [args] The arguments to invoke the method with. - * @returns {Array} Returns the array of results. - * @example - * - * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] - * - * _.invoke([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] - */ - var invoke = restParam(function(collection, path, args) { - var index = -1, - isFunc = typeof path == 'function', - isProp = isKey(path), - result = isArrayLike(collection) ? Array(collection.length) : []; - - baseEach(collection, function(value) { - var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined); - result[++index] = func ? func.apply(value, args) : invokePath(value, path, args); - }); - return result; - }); - - /** - * Creates an array of values by running each element in `collection` through - * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three - * arguments: (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * Many lodash methods are guarded to work as iteratees for methods like - * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. - * - * The guarded methods are: - * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`, - * `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`, - * `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`, - * `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`, - * `sum`, `uniq`, and `words` - * - * @static - * @memberOf _ - * @alias collect - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new mapped array. - * @example - * - * function timesThree(n) { - * return n * 3; - * } - * - * _.map([1, 2], timesThree); - * // => [3, 6] - * - * _.map({ 'a': 1, 'b': 2 }, timesThree); - * // => [3, 6] (iteration order is not guaranteed) - * - * var users = [ - * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * // using the `_.property` callback shorthand - * _.map(users, 'user'); - * // => ['barney', 'fred'] - */ - function map(collection, iteratee, thisArg) { - var func = isArray(collection) ? arrayMap : baseMap; - iteratee = getCallback(iteratee, thisArg, 3); - return func(collection, iteratee); - } - - /** - * Creates an array of elements split into two groups, the first of which - * contains elements `predicate` returns truthy for, while the second of which - * contains elements `predicate` returns falsey for. The predicate is bound - * to `thisArg` and invoked with three arguments: (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the array of grouped elements. - * @example - * - * _.partition([1, 2, 3], function(n) { - * return n % 2; - * }); - * // => [[1, 3], [2]] - * - * _.partition([1.2, 2.3, 3.4], function(n) { - * return this.floor(n) % 2; - * }, Math); - * // => [[1.2, 3.4], [2.3]] - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true }, - * { 'user': 'pebbles', 'age': 1, 'active': false } - * ]; - * - * var mapper = function(array) { - * return _.pluck(array, 'user'); - * }; - * - * // using the `_.matches` callback shorthand - * _.map(_.partition(users, { 'age': 1, 'active': false }), mapper); - * // => [['pebbles'], ['barney', 'fred']] - * - * // using the `_.matchesProperty` callback shorthand - * _.map(_.partition(users, 'active', false), mapper); - * // => [['barney', 'pebbles'], ['fred']] - * - * // using the `_.property` callback shorthand - * _.map(_.partition(users, 'active'), mapper); - * // => [['fred'], ['barney', 'pebbles']] - */ - var partition = createAggregator(function(result, value, key) { - result[key ? 0 : 1].push(value); - }, function() { return [[], []]; }); - - /** - * Gets the property value of `path` from all elements in `collection`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Array|string} path The path of the property to pluck. - * @returns {Array} Returns the property values. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 40 } - * ]; - * - * _.pluck(users, 'user'); - * // => ['barney', 'fred'] - * - * var userIndex = _.indexBy(users, 'user'); - * _.pluck(userIndex, 'age'); - * // => [36, 40] (iteration order is not guaranteed) - */ - function pluck(collection, path) { - return map(collection, property(path)); - } - - /** - * Reduces `collection` to a value which is the accumulated result of running - * each element in `collection` through `iteratee`, where each successive - * invocation is supplied the return value of the previous. If `accumulator` - * is not provided the first element of `collection` is used as the initial - * value. The `iteratee` is bound to `thisArg` and invoked with four arguments: - * (accumulator, value, index|key, collection). - * - * Many lodash methods are guarded to work as iteratees for methods like - * `_.reduce`, `_.reduceRight`, and `_.transform`. - * - * The guarded methods are: - * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `sortByAll`, - * and `sortByOrder` - * - * @static - * @memberOf _ - * @alias foldl, inject - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {*} Returns the accumulated value. - * @example - * - * _.reduce([1, 2], function(total, n) { - * return total + n; - * }); - * // => 3 - * - * _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) { - * result[key] = n * 3; - * return result; - * }, {}); - * // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed) - */ - var reduce = createReduce(arrayReduce, baseEach); - - /** - * This method is like `_.reduce` except that it iterates over elements of - * `collection` from right to left. - * - * @static - * @memberOf _ - * @alias foldr - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {*} Returns the accumulated value. - * @example - * - * var array = [[0, 1], [2, 3], [4, 5]]; - * - * _.reduceRight(array, function(flattened, other) { - * return flattened.concat(other); - * }, []); - * // => [4, 5, 2, 3, 0, 1] - */ - var reduceRight = createReduce(arrayReduceRight, baseEachRight); - - /** - * The opposite of `_.filter`; this method returns the elements of `collection` - * that `predicate` does **not** return truthy for. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Array} Returns the new filtered array. - * @example - * - * _.reject([1, 2, 3, 4], function(n) { - * return n % 2 == 0; - * }); - * // => [1, 3] - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true } - * ]; - * - * // using the `_.matches` callback shorthand - * _.pluck(_.reject(users, { 'age': 40, 'active': true }), 'user'); - * // => ['barney'] - * - * // using the `_.matchesProperty` callback shorthand - * _.pluck(_.reject(users, 'active', false), 'user'); - * // => ['fred'] - * - * // using the `_.property` callback shorthand - * _.pluck(_.reject(users, 'active'), 'user'); - * // => ['barney'] - */ - function reject(collection, predicate, thisArg) { - var func = isArray(collection) ? arrayFilter : baseFilter; - predicate = getCallback(predicate, thisArg, 3); - return func(collection, function(value, index, collection) { - return !predicate(value, index, collection); - }); - } - - /** - * Gets a random element or `n` random elements from a collection. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to sample. - * @param {number} [n] The number of elements to sample. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {*} Returns the random sample(s). - * @example - * - * _.sample([1, 2, 3, 4]); - * // => 2 - * - * _.sample([1, 2, 3, 4], 2); - * // => [3, 1] - */ - function sample(collection, n, guard) { - if (guard ? isIterateeCall(collection, n, guard) : n == null) { - collection = toIterable(collection); - var length = collection.length; - return length > 0 ? collection[baseRandom(0, length - 1)] : undefined; - } - var index = -1, - result = toArray(collection), - length = result.length, - lastIndex = length - 1; - - n = nativeMin(n < 0 ? 0 : (+n || 0), length); - while (++index < n) { - var rand = baseRandom(index, lastIndex), - value = result[rand]; - - result[rand] = result[index]; - result[index] = value; - } - result.length = n; - return result; - } - - /** - * Creates an array of shuffled values, using a version of the - * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to shuffle. - * @returns {Array} Returns the new shuffled array. - * @example - * - * _.shuffle([1, 2, 3, 4]); - * // => [4, 1, 3, 2] - */ - function shuffle(collection) { - return sample(collection, POSITIVE_INFINITY); - } - - /** - * Gets the size of `collection` by returning its length for array-like - * values or the number of own enumerable properties for objects. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to inspect. - * @returns {number} Returns the size of `collection`. - * @example - * - * _.size([1, 2, 3]); - * // => 3 - * - * _.size({ 'a': 1, 'b': 2 }); - * // => 2 - * - * _.size('pebbles'); - * // => 7 - */ - function size(collection) { - var length = collection ? getLength(collection) : 0; - return isLength(length) ? length : keys(collection).length; - } - - /** - * Checks if `predicate` returns truthy for **any** element of `collection`. - * The function returns as soon as it finds a passing value and does not iterate - * over the entire collection. The predicate is bound to `thisArg` and invoked - * with three arguments: (value, index|key, collection). - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @alias any - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - * @example - * - * _.some([null, 0, 'yes', false], Boolean); - * // => true - * - * var users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false } - * ]; - * - * // using the `_.matches` callback shorthand - * _.some(users, { 'user': 'barney', 'active': false }); - * // => false - * - * // using the `_.matchesProperty` callback shorthand - * _.some(users, 'active', false); - * // => true - * - * // using the `_.property` callback shorthand - * _.some(users, 'active'); - * // => true - */ - function some(collection, predicate, thisArg) { - var func = isArray(collection) ? arraySome : baseSome; - if (thisArg && isIterateeCall(collection, predicate, thisArg)) { - predicate = undefined; - } - if (typeof predicate != 'function' || thisArg !== undefined) { - predicate = getCallback(predicate, thisArg, 3); - } - return func(collection, predicate); - } - - /** - * Creates an array of elements, sorted in ascending order by the results of - * running each element in a collection through `iteratee`. This method performs - * a stable sort, that is, it preserves the original sort order of equal elements. - * The `iteratee` is bound to `thisArg` and invoked with three arguments: - * (value, index|key, collection). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Array} Returns the new sorted array. - * @example - * - * _.sortBy([1, 2, 3], function(n) { - * return Math.sin(n); - * }); - * // => [3, 1, 2] - * - * _.sortBy([1, 2, 3], function(n) { - * return this.sin(n); - * }, Math); - * // => [3, 1, 2] - * - * var users = [ - * { 'user': 'fred' }, - * { 'user': 'pebbles' }, - * { 'user': 'barney' } - * ]; - * - * // using the `_.property` callback shorthand - * _.pluck(_.sortBy(users, 'user'), 'user'); - * // => ['barney', 'fred', 'pebbles'] - */ - function sortBy(collection, iteratee, thisArg) { - if (collection == null) { - return []; - } - if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { - iteratee = undefined; - } - var index = -1; - iteratee = getCallback(iteratee, thisArg, 3); - - var result = baseMap(collection, function(value, key, collection) { - return { 'criteria': iteratee(value, key, collection), 'index': ++index, 'value': value }; - }); - return baseSortBy(result, compareAscending); - } - - /** - * This method is like `_.sortBy` except that it can sort by multiple iteratees - * or property names. - * - * If a property name is provided for an iteratee the created `_.property` - * style callback returns the property value of the given element. - * - * If an object is provided for an iteratee the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {...(Function|Function[]|Object|Object[]|string|string[])} iteratees - * The iteratees to sort by, specified as individual values or arrays of values. - * @returns {Array} Returns the new sorted array. - * @example - * - * var users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 36 }, - * { 'user': 'fred', 'age': 42 }, - * { 'user': 'barney', 'age': 34 } - * ]; - * - * _.map(_.sortByAll(users, ['user', 'age']), _.values); - * // => [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]] - * - * _.map(_.sortByAll(users, 'user', function(chr) { - * return Math.floor(chr.age / 10); - * }), _.values); - * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] - */ - var sortByAll = restParam(function(collection, iteratees) { - if (collection == null) { - return []; - } - var guard = iteratees[2]; - if (guard && isIterateeCall(iteratees[0], iteratees[1], guard)) { - iteratees.length = 1; - } - return baseSortByOrder(collection, baseFlatten(iteratees), []); - }); - - /** - * This method is like `_.sortByAll` except that it allows specifying the - * sort orders of the iteratees to sort by. If `orders` is unspecified, all - * values are sorted in ascending order. Otherwise, a value is sorted in - * ascending order if its corresponding order is "asc", and descending if "desc". - * - * If a property name is provided for an iteratee the created `_.property` - * style callback returns the property value of the given element. - * - * If an object is provided for an iteratee the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. - * @param {boolean[]} [orders] The sort orders of `iteratees`. - * @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`. - * @returns {Array} Returns the new sorted array. - * @example - * - * var users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 34 }, - * { 'user': 'fred', 'age': 42 }, - * { 'user': 'barney', 'age': 36 } - * ]; - * - * // sort by `user` in ascending order and by `age` in descending order - * _.map(_.sortByOrder(users, ['user', 'age'], ['asc', 'desc']), _.values); - * // => [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]] - */ - function sortByOrder(collection, iteratees, orders, guard) { - if (collection == null) { - return []; - } - if (guard && isIterateeCall(iteratees, orders, guard)) { - orders = undefined; - } - if (!isArray(iteratees)) { - iteratees = iteratees == null ? [] : [iteratees]; - } - if (!isArray(orders)) { - orders = orders == null ? [] : [orders]; - } - return baseSortByOrder(collection, iteratees, orders); - } - - /** - * Performs a deep comparison between each element in `collection` and the - * source object, returning an array of all elements that have equivalent - * property values. - * - * **Note:** This method supports comparing arrays, booleans, `Date` objects, - * numbers, `Object` objects, regexes, and strings. Objects are compared by - * their own, not inherited, enumerable properties. For comparing a single - * own or inherited property value see `_.matchesProperty`. - * - * @static - * @memberOf _ - * @category Collection - * @param {Array|Object|string} collection The collection to search. - * @param {Object} source The object of property values to match. - * @returns {Array} Returns the new filtered array. - * @example - * - * var users = [ - * { 'user': 'barney', 'age': 36, 'active': false, 'pets': ['hoppy'] }, - * { 'user': 'fred', 'age': 40, 'active': true, 'pets': ['baby puss', 'dino'] } - * ]; - * - * _.pluck(_.where(users, { 'age': 36, 'active': false }), 'user'); - * // => ['barney'] - * - * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user'); - * // => ['fred'] - */ - function where(collection, source) { - return filter(collection, baseMatches(source)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Gets the number of milliseconds that have elapsed since the Unix epoch - * (1 January 1970 00:00:00 UTC). - * - * @static - * @memberOf _ - * @category Date - * @example - * - * _.defer(function(stamp) { - * console.log(_.now() - stamp); - * }, _.now()); - * // => logs the number of milliseconds it took for the deferred function to be invoked - */ - var now = nativeNow || function() { - return new Date().getTime(); - }; - - /*------------------------------------------------------------------------*/ - - /** - * The opposite of `_.before`; this method creates a function that invokes - * `func` once it is called `n` or more times. - * - * @static - * @memberOf _ - * @category Function - * @param {number} n The number of calls before `func` is invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var saves = ['profile', 'settings']; - * - * var done = _.after(saves.length, function() { - * console.log('done saving!'); - * }); - * - * _.forEach(saves, function(type) { - * asyncSave({ 'type': type, 'complete': done }); - * }); - * // => logs 'done saving!' after the two async saves have completed - */ - function after(n, func) { - if (typeof func != 'function') { - if (typeof n == 'function') { - var temp = n; - n = func; - func = temp; - } else { - throw new TypeError(FUNC_ERROR_TEXT); - } - } - n = nativeIsFinite(n = +n) ? n : 0; - return function() { - if (--n < 1) { - return func.apply(this, arguments); - } - }; - } - - /** - * Creates a function that accepts up to `n` arguments ignoring any - * additional arguments. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to cap arguments for. - * @param {number} [n=func.length] The arity cap. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Function} Returns the new function. - * @example - * - * _.map(['6', '8', '10'], _.ary(parseInt, 1)); - * // => [6, 8, 10] - */ - function ary(func, n, guard) { - if (guard && isIterateeCall(func, n, guard)) { - n = undefined; - } - n = (func && n == null) ? func.length : nativeMax(+n || 0, 0); - return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n); - } - - /** - * Creates a function that invokes `func`, with the `this` binding and arguments - * of the created function, while it is called less than `n` times. Subsequent - * calls to the created function return the result of the last `func` invocation. - * - * @static - * @memberOf _ - * @category Function - * @param {number} n The number of calls at which `func` is no longer invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * jQuery('#add').on('click', _.before(5, addContactToList)); - * // => allows adding up to 4 contacts to the list - */ - function before(n, func) { - var result; - if (typeof func != 'function') { - if (typeof n == 'function') { - var temp = n; - n = func; - func = temp; - } else { - throw new TypeError(FUNC_ERROR_TEXT); - } - } - return function() { - if (--n > 0) { - result = func.apply(this, arguments); - } - if (n <= 1) { - func = undefined; - } - return result; - }; - } - - /** - * Creates a function that invokes `func` with the `this` binding of `thisArg` - * and prepends any additional `_.bind` arguments to those provided to the - * bound function. - * - * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, - * may be used as a placeholder for partially applied arguments. - * - * **Note:** Unlike native `Function#bind` this method does not set the "length" - * property of bound functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to bind. - * @param {*} thisArg The `this` binding of `func`. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var greet = function(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * }; - * - * var object = { 'user': 'fred' }; - * - * var bound = _.bind(greet, object, 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * // using placeholders - * var bound = _.bind(greet, object, _, '!'); - * bound('hi'); - * // => 'hi fred!' - */ - var bind = restParam(function(func, thisArg, partials) { - var bitmask = BIND_FLAG; - if (partials.length) { - var holders = replaceHolders(partials, bind.placeholder); - bitmask |= PARTIAL_FLAG; - } - return createWrapper(func, bitmask, thisArg, partials, holders); - }); - - /** - * Binds methods of an object to the object itself, overwriting the existing - * method. Method names may be specified as individual arguments or as arrays - * of method names. If no method names are provided all enumerable function - * properties, own and inherited, of `object` are bound. - * - * **Note:** This method does not set the "length" property of bound functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Object} object The object to bind and assign the bound methods to. - * @param {...(string|string[])} [methodNames] The object method names to bind, - * specified as individual method names or arrays of method names. - * @returns {Object} Returns `object`. - * @example - * - * var view = { - * 'label': 'docs', - * 'onClick': function() { - * console.log('clicked ' + this.label); - * } - * }; - * - * _.bindAll(view); - * jQuery('#docs').on('click', view.onClick); - * // => logs 'clicked docs' when the element is clicked - */ - var bindAll = restParam(function(object, methodNames) { - methodNames = methodNames.length ? baseFlatten(methodNames) : functions(object); - - var index = -1, - length = methodNames.length; - - while (++index < length) { - var key = methodNames[index]; - object[key] = createWrapper(object[key], BIND_FLAG, object); - } - return object; - }); - - /** - * Creates a function that invokes the method at `object[key]` and prepends - * any additional `_.bindKey` arguments to those provided to the bound function. - * - * This method differs from `_.bind` by allowing bound functions to reference - * methods that may be redefined or don't yet exist. - * See [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) - * for more details. - * - * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * @static - * @memberOf _ - * @category Function - * @param {Object} object The object the method belongs to. - * @param {string} key The key of the method. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var object = { - * 'user': 'fred', - * 'greet': function(greeting, punctuation) { - * return greeting + ' ' + this.user + punctuation; - * } - * }; - * - * var bound = _.bindKey(object, 'greet', 'hi'); - * bound('!'); - * // => 'hi fred!' - * - * object.greet = function(greeting, punctuation) { - * return greeting + 'ya ' + this.user + punctuation; - * }; - * - * bound('!'); - * // => 'hiya fred!' - * - * // using placeholders - * var bound = _.bindKey(object, 'greet', _, '!'); - * bound('hi'); - * // => 'hiya fred!' - */ - var bindKey = restParam(function(object, key, partials) { - var bitmask = BIND_FLAG | BIND_KEY_FLAG; - if (partials.length) { - var holders = replaceHolders(partials, bindKey.placeholder); - bitmask |= PARTIAL_FLAG; - } - return createWrapper(key, bitmask, object, partials, holders); - }); - - /** - * Creates a function that accepts one or more arguments of `func` that when - * called either invokes `func` returning its result, if all `func` arguments - * have been provided, or returns a function that accepts one or more of the - * remaining `func` arguments, and so on. The arity of `func` may be specified - * if `func.length` is not sufficient. - * - * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, - * may be used as a placeholder for provided arguments. - * - * **Note:** This method does not set the "length" property of curried functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Function} Returns the new curried function. - * @example - * - * var abc = function(a, b, c) { - * return [a, b, c]; - * }; - * - * var curried = _.curry(abc); - * - * curried(1)(2)(3); - * // => [1, 2, 3] - * - * curried(1, 2)(3); - * // => [1, 2, 3] - * - * curried(1, 2, 3); - * // => [1, 2, 3] - * - * // using placeholders - * curried(1)(_, 3)(2); - * // => [1, 2, 3] - */ - var curry = createCurry(CURRY_FLAG); - - /** - * This method is like `_.curry` except that arguments are applied to `func` - * in the manner of `_.partialRight` instead of `_.partial`. - * - * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for provided arguments. - * - * **Note:** This method does not set the "length" property of curried functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Function} Returns the new curried function. - * @example - * - * var abc = function(a, b, c) { - * return [a, b, c]; - * }; - * - * var curried = _.curryRight(abc); - * - * curried(3)(2)(1); - * // => [1, 2, 3] - * - * curried(2, 3)(1); - * // => [1, 2, 3] - * - * curried(1, 2, 3); - * // => [1, 2, 3] - * - * // using placeholders - * curried(3)(1, _)(2); - * // => [1, 2, 3] - */ - var curryRight = createCurry(CURRY_RIGHT_FLAG); - - /** - * Creates a debounced function that delays invoking `func` until after `wait` - * milliseconds have elapsed since the last time the debounced function was - * invoked. The debounced function comes with a `cancel` method to cancel - * delayed invocations. Provide an options object to indicate that `func` - * should be invoked on the leading and/or trailing edge of the `wait` timeout. - * Subsequent calls to the debounced function return the result of the last - * `func` invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked - * on the trailing edge of the timeout only if the the debounced function is - * invoked more than once during the `wait` timeout. - * - * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) - * for details over the differences between `_.debounce` and `_.throttle`. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to debounce. - * @param {number} [wait=0] The number of milliseconds to delay. - * @param {Object} [options] The options object. - * @param {boolean} [options.leading=false] Specify invoking on the leading - * edge of the timeout. - * @param {number} [options.maxWait] The maximum time `func` is allowed to be - * delayed before it is invoked. - * @param {boolean} [options.trailing=true] Specify invoking on the trailing - * edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // avoid costly calculations while the window size is in flux - * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); - * - * // invoke `sendMail` when the click event is fired, debouncing subsequent calls - * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * })); - * - * // ensure `batchLog` is invoked once after 1 second of debounced calls - * var source = new EventSource('/stream'); - * jQuery(source).on('message', _.debounce(batchLog, 250, { - * 'maxWait': 1000 - * })); - * - * // cancel a debounced call - * var todoChanges = _.debounce(batchLog, 1000); - * Object.observe(models.todo, todoChanges); - * - * Object.observe(models, function(changes) { - * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) { - * todoChanges.cancel(); - * } - * }, ['delete']); - * - * // ...at some point `models.todo` is changed - * models.todo.completed = true; - * - * // ...before 1 second has passed `models.todo` is deleted - * // which cancels the debounced `todoChanges` call - * delete models.todo; - */ - function debounce(func, wait, options) { - var args, - maxTimeoutId, - result, - stamp, - thisArg, - timeoutId, - trailingCall, - lastCalled = 0, - maxWait = false, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - wait = wait < 0 ? 0 : (+wait || 0); - if (options === true) { - var leading = true; - trailing = false; - } else if (isObject(options)) { - leading = !!options.leading; - maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait); - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - - function cancel() { - if (timeoutId) { - clearTimeout(timeoutId); - } - if (maxTimeoutId) { - clearTimeout(maxTimeoutId); - } - lastCalled = 0; - maxTimeoutId = timeoutId = trailingCall = undefined; - } - - function complete(isCalled, id) { - if (id) { - clearTimeout(id); - } - maxTimeoutId = timeoutId = trailingCall = undefined; - if (isCalled) { - lastCalled = now(); - result = func.apply(thisArg, args); - if (!timeoutId && !maxTimeoutId) { - args = thisArg = undefined; - } - } - } - - function delayed() { - var remaining = wait - (now() - stamp); - if (remaining <= 0 || remaining > wait) { - complete(trailingCall, maxTimeoutId); - } else { - timeoutId = setTimeout(delayed, remaining); - } - } - - function maxDelayed() { - complete(trailing, timeoutId); - } - - function debounced() { - args = arguments; - stamp = now(); - thisArg = this; - trailingCall = trailing && (timeoutId || !leading); - - if (maxWait === false) { - var leadingCall = leading && !timeoutId; - } else { - if (!maxTimeoutId && !leading) { - lastCalled = stamp; - } - var remaining = maxWait - (stamp - lastCalled), - isCalled = remaining <= 0 || remaining > maxWait; - - if (isCalled) { - if (maxTimeoutId) { - maxTimeoutId = clearTimeout(maxTimeoutId); - } - lastCalled = stamp; - result = func.apply(thisArg, args); - } - else if (!maxTimeoutId) { - maxTimeoutId = setTimeout(maxDelayed, remaining); - } - } - if (isCalled && timeoutId) { - timeoutId = clearTimeout(timeoutId); - } - else if (!timeoutId && wait !== maxWait) { - timeoutId = setTimeout(delayed, wait); - } - if (leadingCall) { - isCalled = true; - result = func.apply(thisArg, args); - } - if (isCalled && !timeoutId && !maxTimeoutId) { - args = thisArg = undefined; - } - return result; - } - debounced.cancel = cancel; - return debounced; - } - - /** - * Defers invoking the `func` until the current call stack has cleared. Any - * additional arguments are provided to `func` when it is invoked. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to defer. - * @param {...*} [args] The arguments to invoke the function with. - * @returns {number} Returns the timer id. - * @example - * - * _.defer(function(text) { - * console.log(text); - * }, 'deferred'); - * // logs 'deferred' after one or more milliseconds - */ - var defer = restParam(function(func, args) { - return baseDelay(func, 1, args); - }); - - /** - * Invokes `func` after `wait` milliseconds. Any additional arguments are - * provided to `func` when it is invoked. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {...*} [args] The arguments to invoke the function with. - * @returns {number} Returns the timer id. - * @example - * - * _.delay(function(text) { - * console.log(text); - * }, 1000, 'later'); - * // => logs 'later' after one second - */ - var delay = restParam(function(func, wait, args) { - return baseDelay(func, wait, args); - }); - - /** - * Creates a function that returns the result of invoking the provided - * functions with the `this` binding of the created function, where each - * successive invocation is supplied the return value of the previous. - * - * @static - * @memberOf _ - * @category Function - * @param {...Function} [funcs] Functions to invoke. - * @returns {Function} Returns the new function. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var addSquare = _.flow(_.add, square); - * addSquare(1, 2); - * // => 9 - */ - var flow = createFlow(); - - /** - * This method is like `_.flow` except that it creates a function that - * invokes the provided functions from right to left. - * - * @static - * @memberOf _ - * @alias backflow, compose - * @category Function - * @param {...Function} [funcs] Functions to invoke. - * @returns {Function} Returns the new function. - * @example - * - * function square(n) { - * return n * n; - * } - * - * var addSquare = _.flowRight(square, _.add); - * addSquare(1, 2); - * // => 9 - */ - var flowRight = createFlow(true); - - /** - * Creates a function that memoizes the result of `func`. If `resolver` is - * provided it determines the cache key for storing the result based on the - * arguments provided to the memoized function. By default, the first argument - * provided to the memoized function is coerced to a string and used as the - * cache key. The `func` is invoked with the `this` binding of the memoized - * function. - * - * **Note:** The cache is exposed as the `cache` property on the memoized - * function. Its creation may be customized by replacing the `_.memoize.Cache` - * constructor with one whose instances implement the [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object) - * method interface of `get`, `has`, and `set`. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] The function to resolve the cache key. - * @returns {Function} Returns the new memoizing function. - * @example - * - * var upperCase = _.memoize(function(string) { - * return string.toUpperCase(); - * }); - * - * upperCase('fred'); - * // => 'FRED' - * - * // modifying the result cache - * upperCase.cache.set('fred', 'BARNEY'); - * upperCase('fred'); - * // => 'BARNEY' - * - * // replacing `_.memoize.Cache` - * var object = { 'user': 'fred' }; - * var other = { 'user': 'barney' }; - * var identity = _.memoize(_.identity); - * - * identity(object); - * // => { 'user': 'fred' } - * identity(other); - * // => { 'user': 'fred' } - * - * _.memoize.Cache = WeakMap; - * var identity = _.memoize(_.identity); - * - * identity(object); - * // => { 'user': 'fred' } - * identity(other); - * // => { 'user': 'barney' } - */ - function memoize(func, resolver) { - if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { - throw new TypeError(FUNC_ERROR_TEXT); - } - var memoized = function() { - var args = arguments, - key = resolver ? resolver.apply(this, args) : args[0], - cache = memoized.cache; - - if (cache.has(key)) { - return cache.get(key); - } - var result = func.apply(this, args); - memoized.cache = cache.set(key, result); - return result; - }; - memoized.cache = new memoize.Cache; - return memoized; - } - - /** - * Creates a function that runs each argument through a corresponding - * transform function. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to wrap. - * @param {...(Function|Function[])} [transforms] The functions to transform - * arguments, specified as individual functions or arrays of functions. - * @returns {Function} Returns the new function. - * @example - * - * function doubled(n) { - * return n * 2; - * } - * - * function square(n) { - * return n * n; - * } - * - * var modded = _.modArgs(function(x, y) { - * return [x, y]; - * }, square, doubled); - * - * modded(1, 2); - * // => [1, 4] - * - * modded(5, 10); - * // => [25, 20] - */ - var modArgs = restParam(function(func, transforms) { - transforms = baseFlatten(transforms); - if (typeof func != 'function' || !arrayEvery(transforms, baseIsFunction)) { - throw new TypeError(FUNC_ERROR_TEXT); - } - var length = transforms.length; - return restParam(function(args) { - var index = nativeMin(args.length, length); - while (index--) { - args[index] = transforms[index](args[index]); - } - return func.apply(this, args); - }); - }); - - /** - * Creates a function that negates the result of the predicate `func`. The - * `func` predicate is invoked with the `this` binding and arguments of the - * created function. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} predicate The predicate to negate. - * @returns {Function} Returns the new function. - * @example - * - * function isEven(n) { - * return n % 2 == 0; - * } - * - * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); - * // => [1, 3, 5] - */ - function negate(predicate) { - if (typeof predicate != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - return function() { - return !predicate.apply(this, arguments); - }; - } - - /** - * Creates a function that is restricted to invoking `func` once. Repeat calls - * to the function return the value of the first call. The `func` is invoked - * with the `this` binding and arguments of the created function. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var initialize = _.once(createApplication); - * initialize(); - * initialize(); - * // `initialize` invokes `createApplication` once - */ - function once(func) { - return before(2, func); - } - - /** - * Creates a function that invokes `func` with `partial` arguments prepended - * to those provided to the new function. This method is like `_.bind` except - * it does **not** alter the `this` binding. - * - * The `_.partial.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * **Note:** This method does not set the "length" property of partially - * applied functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * var greet = function(greeting, name) { - * return greeting + ' ' + name; - * }; - * - * var sayHelloTo = _.partial(greet, 'hello'); - * sayHelloTo('fred'); - * // => 'hello fred' - * - * // using placeholders - * var greetFred = _.partial(greet, _, 'fred'); - * greetFred('hi'); - * // => 'hi fred' - */ - var partial = createPartial(PARTIAL_FLAG); - - /** - * This method is like `_.partial` except that partially applied arguments - * are appended to those provided to the new function. - * - * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic - * builds, may be used as a placeholder for partially applied arguments. - * - * **Note:** This method does not set the "length" property of partially - * applied functions. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [partials] The arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * var greet = function(greeting, name) { - * return greeting + ' ' + name; - * }; - * - * var greetFred = _.partialRight(greet, 'fred'); - * greetFred('hi'); - * // => 'hi fred' - * - * // using placeholders - * var sayHelloTo = _.partialRight(greet, 'hello', _); - * sayHelloTo('fred'); - * // => 'hello fred' - */ - var partialRight = createPartial(PARTIAL_RIGHT_FLAG); - - /** - * Creates a function that invokes `func` with arguments arranged according - * to the specified indexes where the argument value at the first index is - * provided as the first argument, the argument value at the second index is - * provided as the second argument, and so on. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to rearrange arguments for. - * @param {...(number|number[])} indexes The arranged argument indexes, - * specified as individual indexes or arrays of indexes. - * @returns {Function} Returns the new function. - * @example - * - * var rearged = _.rearg(function(a, b, c) { - * return [a, b, c]; - * }, 2, 0, 1); - * - * rearged('b', 'c', 'a') - * // => ['a', 'b', 'c'] - * - * var map = _.rearg(_.map, [1, 0]); - * map(function(n) { - * return n * 3; - * }, [1, 2, 3]); - * // => [3, 6, 9] - */ - var rearg = restParam(function(func, indexes) { - return createWrapper(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes)); - }); - - /** - * Creates a function that invokes `func` with the `this` binding of the - * created function and arguments from `start` and beyond provided as an array. - * - * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters). - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to apply a rest parameter to. - * @param {number} [start=func.length-1] The start position of the rest parameter. - * @returns {Function} Returns the new function. - * @example - * - * var say = _.restParam(function(what, names) { - * return what + ' ' + _.initial(names).join(', ') + - * (_.size(names) > 1 ? ', & ' : '') + _.last(names); - * }); - * - * say('hello', 'fred', 'barney', 'pebbles'); - * // => 'hello fred, barney, & pebbles' - */ - function restParam(func, start) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); - return function() { - var args = arguments, - index = -1, - length = nativeMax(args.length - start, 0), - rest = Array(length); - - while (++index < length) { - rest[index] = args[start + index]; - } - switch (start) { - case 0: return func.call(this, rest); - case 1: return func.call(this, args[0], rest); - case 2: return func.call(this, args[0], args[1], rest); - } - var otherArgs = Array(start + 1); - index = -1; - while (++index < start) { - otherArgs[index] = args[index]; - } - otherArgs[start] = rest; - return func.apply(this, otherArgs); - }; - } - - /** - * Creates a function that invokes `func` with the `this` binding of the created - * function and an array of arguments much like [`Function#apply`](https://es5.github.io/#x15.3.4.3). - * - * **Note:** This method is based on the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator). - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to spread arguments over. - * @returns {Function} Returns the new function. - * @example - * - * var say = _.spread(function(who, what) { - * return who + ' says ' + what; - * }); - * - * say(['fred', 'hello']); - * // => 'fred says hello' - * - * // with a Promise - * var numbers = Promise.all([ - * Promise.resolve(40), - * Promise.resolve(36) - * ]); - * - * numbers.then(_.spread(function(x, y) { - * return x + y; - * })); - * // => a Promise of 76 - */ - function spread(func) { - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - return function(array) { - return func.apply(this, array); - }; - } - - /** - * Creates a throttled function that only invokes `func` at most once per - * every `wait` milliseconds. The throttled function comes with a `cancel` - * method to cancel delayed invocations. Provide an options object to indicate - * that `func` should be invoked on the leading and/or trailing edge of the - * `wait` timeout. Subsequent calls to the throttled function return the - * result of the last `func` call. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked - * on the trailing edge of the timeout only if the the throttled function is - * invoked more than once during the `wait` timeout. - * - * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) - * for details over the differences between `_.throttle` and `_.debounce`. - * - * @static - * @memberOf _ - * @category Function - * @param {Function} func The function to throttle. - * @param {number} [wait=0] The number of milliseconds to throttle invocations to. - * @param {Object} [options] The options object. - * @param {boolean} [options.leading=true] Specify invoking on the leading - * edge of the timeout. - * @param {boolean} [options.trailing=true] Specify invoking on the trailing - * edge of the timeout. - * @returns {Function} Returns the new throttled function. - * @example - * - * // avoid excessively updating the position while scrolling - * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); - * - * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes - * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { - * 'trailing': false - * })); - * - * // cancel a trailing throttled call - * jQuery(window).on('popstate', throttled.cancel); - */ - function throttle(func, wait, options) { - var leading = true, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - if (options === false) { - leading = false; - } else if (isObject(options)) { - leading = 'leading' in options ? !!options.leading : leading; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing }); - } - - /** - * Creates a function that provides `value` to the wrapper function as its - * first argument. Any additional arguments provided to the function are - * appended to those provided to the wrapper function. The wrapper is invoked - * with the `this` binding of the created function. - * - * @static - * @memberOf _ - * @category Function - * @param {*} value The value to wrap. - * @param {Function} wrapper The wrapper function. - * @returns {Function} Returns the new function. - * @example - * - * var p = _.wrap(_.escape, function(func, text) { - * return '

' + func(text) + '

'; - * }); - * - * p('fred, barney, & pebbles'); - * // => '

fred, barney, & pebbles

' - */ - function wrap(value, wrapper) { - wrapper = wrapper == null ? identity : wrapper; - return createWrapper(wrapper, PARTIAL_FLAG, undefined, [value], []); - } - - /*------------------------------------------------------------------------*/ - - /** - * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned, - * otherwise they are assigned by reference. If `customizer` is provided it is - * invoked to produce the cloned values. If `customizer` returns `undefined` - * cloning is handled by the method instead. The `customizer` is bound to - * `thisArg` and invoked with two argument; (value [, index|key, object]). - * - * **Note:** This method is loosely based on the - * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). - * The enumerable properties of `arguments` objects and objects created by - * constructors other than `Object` are cloned to plain `Object` objects. An - * empty object is returned for uncloneable values such as functions, DOM nodes, - * Maps, Sets, and WeakMaps. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @param {Function} [customizer] The function to customize cloning values. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {*} Returns the cloned value. - * @example - * - * var users = [ - * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * var shallow = _.clone(users); - * shallow[0] === users[0]; - * // => true - * - * var deep = _.clone(users, true); - * deep[0] === users[0]; - * // => false - * - * // using a customizer callback - * var el = _.clone(document.body, function(value) { - * if (_.isElement(value)) { - * return value.cloneNode(false); - * } - * }); - * - * el === document.body - * // => false - * el.nodeName - * // => BODY - * el.childNodes.length; - * // => 0 - */ - function clone(value, isDeep, customizer, thisArg) { - if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) { - isDeep = false; - } - else if (typeof isDeep == 'function') { - thisArg = customizer; - customizer = isDeep; - isDeep = false; - } - return typeof customizer == 'function' - ? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1)) - : baseClone(value, isDeep); - } - - /** - * Creates a deep clone of `value`. If `customizer` is provided it is invoked - * to produce the cloned values. If `customizer` returns `undefined` cloning - * is handled by the method instead. The `customizer` is bound to `thisArg` - * and invoked with two argument; (value [, index|key, object]). - * - * **Note:** This method is loosely based on the - * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). - * The enumerable properties of `arguments` objects and objects created by - * constructors other than `Object` are cloned to plain `Object` objects. An - * empty object is returned for uncloneable values such as functions, DOM nodes, - * Maps, Sets, and WeakMaps. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to deep clone. - * @param {Function} [customizer] The function to customize cloning values. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {*} Returns the deep cloned value. - * @example - * - * var users = [ - * { 'user': 'barney' }, - * { 'user': 'fred' } - * ]; - * - * var deep = _.cloneDeep(users); - * deep[0] === users[0]; - * // => false - * - * // using a customizer callback - * var el = _.cloneDeep(document.body, function(value) { - * if (_.isElement(value)) { - * return value.cloneNode(true); - * } - * }); - * - * el === document.body - * // => false - * el.nodeName - * // => BODY - * el.childNodes.length; - * // => 20 - */ - function cloneDeep(value, customizer, thisArg) { - return typeof customizer == 'function' - ? baseClone(value, true, bindCallback(customizer, thisArg, 1)) - : baseClone(value, true); - } - - /** - * Checks if `value` is greater than `other`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, else `false`. - * @example - * - * _.gt(3, 1); - * // => true - * - * _.gt(3, 3); - * // => false - * - * _.gt(1, 3); - * // => false - */ - function gt(value, other) { - return value > other; - } - - /** - * Checks if `value` is greater than or equal to `other`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than or equal to `other`, else `false`. - * @example - * - * _.gte(3, 1); - * // => true - * - * _.gte(3, 3); - * // => true - * - * _.gte(1, 3); - * // => false - */ - function gte(value, other) { - return value >= other; - } - - /** - * Checks if `value` is classified as an `arguments` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isArguments(function() { return arguments; }()); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ - function isArguments(value) { - return isObjectLike(value) && isArrayLike(value) && - hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); - } - - /** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(function() { return arguments; }()); - * // => false - */ - var isArray = nativeIsArray || function(value) { - return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; - }; - - /** - * Checks if `value` is classified as a boolean primitive or object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isBoolean(false); - * // => true - * - * _.isBoolean(null); - * // => false - */ - function isBoolean(value) { - return value === true || value === false || (isObjectLike(value) && objToString.call(value) == boolTag); - } - - /** - * Checks if `value` is classified as a `Date` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isDate(new Date); - * // => true - * - * _.isDate('Mon April 23 2012'); - * // => false - */ - function isDate(value) { - return isObjectLike(value) && objToString.call(value) == dateTag; - } - - /** - * Checks if `value` is a DOM element. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. - * @example - * - * _.isElement(document.body); - * // => true - * - * _.isElement(''); - * // => false - */ - function isElement(value) { - return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value); - } - - /** - * Checks if `value` is empty. A value is considered empty unless it is an - * `arguments` object, array, string, or jQuery-like collection with a length - * greater than `0` or an object with own enumerable properties. - * - * @static - * @memberOf _ - * @category Lang - * @param {Array|Object|string} value The value to inspect. - * @returns {boolean} Returns `true` if `value` is empty, else `false`. - * @example - * - * _.isEmpty(null); - * // => true - * - * _.isEmpty(true); - * // => true - * - * _.isEmpty(1); - * // => true - * - * _.isEmpty([1, 2, 3]); - * // => false - * - * _.isEmpty({ 'a': 1 }); - * // => false - */ - function isEmpty(value) { - if (value == null) { - return true; - } - if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) || - (isObjectLike(value) && isFunction(value.splice)))) { - return !value.length; - } - return !keys(value).length; - } - - /** - * Performs a deep comparison between two values to determine if they are - * equivalent. If `customizer` is provided it is invoked to compare values. - * If `customizer` returns `undefined` comparisons are handled by the method - * instead. The `customizer` is bound to `thisArg` and invoked with three - * arguments: (value, other [, index|key]). - * - * **Note:** This method supports comparing arrays, booleans, `Date` objects, - * numbers, `Object` objects, regexes, and strings. Objects are compared by - * their own, not inherited, enumerable properties. Functions and DOM nodes - * are **not** supported. Provide a customizer function to extend support - * for comparing other values. - * - * @static - * @memberOf _ - * @alias eq - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize value comparisons. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'user': 'fred' }; - * var other = { 'user': 'fred' }; - * - * object == other; - * // => false - * - * _.isEqual(object, other); - * // => true - * - * // using a customizer callback - * var array = ['hello', 'goodbye']; - * var other = ['hi', 'goodbye']; - * - * _.isEqual(array, other, function(value, other) { - * if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) { - * return true; - * } - * }); - * // => true - */ - function isEqual(value, other, customizer, thisArg) { - customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; - var result = customizer ? customizer(value, other) : undefined; - return result === undefined ? baseIsEqual(value, other, customizer) : !!result; - } - - /** - * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, - * `SyntaxError`, `TypeError`, or `URIError` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an error object, else `false`. - * @example - * - * _.isError(new Error); - * // => true - * - * _.isError(Error); - * // => false - */ - function isError(value) { - return isObjectLike(value) && typeof value.message == 'string' && objToString.call(value) == errorTag; - } - - /** - * Checks if `value` is a finite primitive number. - * - * **Note:** This method is based on [`Number.isFinite`](http://ecma-international.org/ecma-262/6.0/#sec-number.isfinite). - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. - * @example - * - * _.isFinite(10); - * // => true - * - * _.isFinite('10'); - * // => false - * - * _.isFinite(true); - * // => false - * - * _.isFinite(Object(10)); - * // => false - * - * _.isFinite(Infinity); - * // => false - */ - function isFinite(value) { - return typeof value == 'number' && nativeIsFinite(value); - } - - /** - * Checks if `value` is classified as a `Function` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isFunction(_); - * // => true - * - * _.isFunction(/abc/); - * // => false - */ - function isFunction(value) { - // The use of `Object#toString` avoids issues with the `typeof` operator - // in older versions of Chrome and Safari which return 'function' for regexes - // and Safari 8 equivalents which return 'object' for typed array constructors. - return isObject(value) && objToString.call(value) == funcTag; - } - - /** - * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. - * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(1); - * // => false - */ - function isObject(value) { - // Avoid a V8 JIT bug in Chrome 19-20. - // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. - var type = typeof value; - return !!value && (type == 'object' || type == 'function'); - } - - /** - * Performs a deep comparison between `object` and `source` to determine if - * `object` contains equivalent property values. If `customizer` is provided - * it is invoked to compare values. If `customizer` returns `undefined` - * comparisons are handled by the method instead. The `customizer` is bound - * to `thisArg` and invoked with three arguments: (value, other, index|key). - * - * **Note:** This method supports comparing properties of arrays, booleans, - * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions - * and DOM nodes are **not** supported. Provide a customizer function to extend - * support for comparing other values. - * - * @static - * @memberOf _ - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Function} [customizer] The function to customize value comparisons. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * var object = { 'user': 'fred', 'age': 40 }; - * - * _.isMatch(object, { 'age': 40 }); - * // => true - * - * _.isMatch(object, { 'age': 36 }); - * // => false - * - * // using a customizer callback - * var object = { 'greeting': 'hello' }; - * var source = { 'greeting': 'hi' }; - * - * _.isMatch(object, source, function(value, other) { - * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined; - * }); - * // => true - */ - function isMatch(object, source, customizer, thisArg) { - customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; - return baseIsMatch(object, getMatchData(source), customizer); - } - - /** - * Checks if `value` is `NaN`. - * - * **Note:** This method is not the same as [`isNaN`](https://es5.github.io/#x15.1.2.4) - * which returns `true` for `undefined` and other non-numeric values. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false - */ - function isNaN(value) { - // An `NaN` primitive is the only value that is not equal to itself. - // Perform the `toStringTag` check first to avoid errors with some host objects in IE. - return isNumber(value) && value != +value; - } - - /** - * Checks if `value` is a native function. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, else `false`. - * @example - * - * _.isNative(Array.prototype.push); - * // => true - * - * _.isNative(_); - * // => false - */ - function isNative(value) { - if (value == null) { - return false; - } - if (isFunction(value)) { - return reIsNative.test(fnToString.call(value)); - } - return isObjectLike(value) && reIsHostCtor.test(value); - } - - /** - * Checks if `value` is `null`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `null`, else `false`. - * @example - * - * _.isNull(null); - * // => true - * - * _.isNull(void 0); - * // => false - */ - function isNull(value) { - return value === null; - } - - /** - * Checks if `value` is classified as a `Number` primitive or object. - * - * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified - * as numbers, use the `_.isFinite` method. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isNumber(8.4); - * // => true - * - * _.isNumber(NaN); - * // => true - * - * _.isNumber('8.4'); - * // => false - */ - function isNumber(value) { - return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag); - } - - /** - * Checks if `value` is a plain object, that is, an object created by the - * `Object` constructor or one with a `[[Prototype]]` of `null`. - * - * **Note:** This method assumes objects created by the `Object` constructor - * have no inherited enumerable properties. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Foo() { - * this.a = 1; - * } - * - * _.isPlainObject(new Foo); - * // => false - * - * _.isPlainObject([1, 2, 3]); - * // => false - * - * _.isPlainObject({ 'x': 0, 'y': 0 }); - * // => true - * - * _.isPlainObject(Object.create(null)); - * // => true - */ - function isPlainObject(value) { - var Ctor; - - // Exit early for non `Object` objects. - if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isArguments(value)) || - (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) { - return false; - } - // IE < 9 iterates inherited properties before own properties. If the first - // iterated property is an object's own property then there are no inherited - // enumerable properties. - var result; - // In most environments an object's own properties are iterated before - // its inherited properties. If the last iterated property is an object's - // own property then there are no inherited enumerable properties. - baseForIn(value, function(subValue, key) { - result = key; - }); - return result === undefined || hasOwnProperty.call(value, result); - } - - /** - * Checks if `value` is classified as a `RegExp` object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isRegExp(/abc/); - * // => true - * - * _.isRegExp('/abc/'); - * // => false - */ - function isRegExp(value) { - return isObject(value) && objToString.call(value) == regexpTag; - } - - /** - * Checks if `value` is classified as a `String` primitive or object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isString('abc'); - * // => true - * - * _.isString(1); - * // => false - */ - function isString(value) { - return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag); - } - - /** - * Checks if `value` is classified as a typed array. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. - * @example - * - * _.isTypedArray(new Uint8Array); - * // => true - * - * _.isTypedArray([]); - * // => false - */ - function isTypedArray(value) { - return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)]; - } - - /** - * Checks if `value` is `undefined`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. - * @example - * - * _.isUndefined(void 0); - * // => true - * - * _.isUndefined(null); - * // => false - */ - function isUndefined(value) { - return value === undefined; - } - - /** - * Checks if `value` is less than `other`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than `other`, else `false`. - * @example - * - * _.lt(1, 3); - * // => true - * - * _.lt(3, 3); - * // => false - * - * _.lt(3, 1); - * // => false - */ - function lt(value, other) { - return value < other; - } - - /** - * Checks if `value` is less than or equal to `other`. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than or equal to `other`, else `false`. - * @example - * - * _.lte(1, 3); - * // => true - * - * _.lte(3, 3); - * // => true - * - * _.lte(3, 1); - * // => false - */ - function lte(value, other) { - return value <= other; - } - - /** - * Converts `value` to an array. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to convert. - * @returns {Array} Returns the converted array. - * @example - * - * (function() { - * return _.toArray(arguments).slice(1); - * }(1, 2, 3)); - * // => [2, 3] - */ - function toArray(value) { - var length = value ? getLength(value) : 0; - if (!isLength(length)) { - return values(value); - } - if (!length) { - return []; - } - return arrayCopy(value); - } - - /** - * Converts `value` to a plain object flattening inherited enumerable - * properties of `value` to own properties of the plain object. - * - * @static - * @memberOf _ - * @category Lang - * @param {*} value The value to convert. - * @returns {Object} Returns the converted plain object. - * @example - * - * function Foo() { - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.assign({ 'a': 1 }, new Foo); - * // => { 'a': 1, 'b': 2 } - * - * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); - * // => { 'a': 1, 'b': 2, 'c': 3 } - */ - function toPlainObject(value) { - return baseCopy(value, keysIn(value)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Recursively merges own enumerable properties of the source object(s), that - * don't resolve to `undefined` into the destination object. Subsequent sources - * overwrite property assignments of previous sources. If `customizer` is - * provided it is invoked to produce the merged values of the destination and - * source properties. If `customizer` returns `undefined` merging is handled - * by the method instead. The `customizer` is bound to `thisArg` and invoked - * with five arguments: (objectValue, sourceValue, key, object, source). - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {Object} Returns `object`. - * @example - * - * var users = { - * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] - * }; - * - * var ages = { - * 'data': [{ 'age': 36 }, { 'age': 40 }] - * }; - * - * _.merge(users, ages); - * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } - * - * // using a customizer callback - * var object = { - * 'fruits': ['apple'], - * 'vegetables': ['beet'] - * }; - * - * var other = { - * 'fruits': ['banana'], - * 'vegetables': ['carrot'] - * }; - * - * _.merge(object, other, function(a, b) { - * if (_.isArray(a)) { - * return a.concat(b); - * } - * }); - * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } - */ - var merge = createAssigner(baseMerge); - - /** - * Assigns own enumerable properties of source object(s) to the destination - * object. Subsequent sources overwrite property assignments of previous sources. - * If `customizer` is provided it is invoked to produce the assigned values. - * The `customizer` is bound to `thisArg` and invoked with five arguments: - * (objectValue, sourceValue, key, object, source). - * - * **Note:** This method mutates `object` and is based on - * [`Object.assign`](http://ecma-international.org/ecma-262/6.0/#sec-object.assign). - * - * @static - * @memberOf _ - * @alias extend - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @param {Function} [customizer] The function to customize assigned values. - * @param {*} [thisArg] The `this` binding of `customizer`. - * @returns {Object} Returns `object`. - * @example - * - * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' }); - * // => { 'user': 'fred', 'age': 40 } - * - * // using a customizer callback - * var defaults = _.partialRight(_.assign, function(value, other) { - * return _.isUndefined(value) ? other : value; - * }); - * - * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); - * // => { 'user': 'barney', 'age': 36 } - */ - var assign = createAssigner(function(object, source, customizer) { - return customizer - ? assignWith(object, source, customizer) - : baseAssign(object, source); - }); - - /** - * Creates an object that inherits from the given `prototype` object. If a - * `properties` object is provided its own enumerable properties are assigned - * to the created object. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} prototype The object to inherit from. - * @param {Object} [properties] The properties to assign to the object. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Object} Returns the new object. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * function Circle() { - * Shape.call(this); - * } - * - * Circle.prototype = _.create(Shape.prototype, { - * 'constructor': Circle - * }); - * - * var circle = new Circle; - * circle instanceof Circle; - * // => true - * - * circle instanceof Shape; - * // => true - */ - function create(prototype, properties, guard) { - var result = baseCreate(prototype); - if (guard && isIterateeCall(prototype, properties, guard)) { - properties = undefined; - } - return properties ? baseAssign(result, properties) : result; - } - - /** - * Assigns own enumerable properties of source object(s) to the destination - * object for all destination properties that resolve to `undefined`. Once a - * property is set, additional values of the same property are ignored. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); - * // => { 'user': 'barney', 'age': 36 } - */ - var defaults = createDefaults(assign, assignDefaults); - - /** - * This method is like `_.defaults` except that it recursively assigns - * default properties. - * - * **Note:** This method mutates `object`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } }); - * // => { 'user': { 'name': 'barney', 'age': 36 } } - * - */ - var defaultsDeep = createDefaults(merge, mergeDefaults); - - /** - * This method is like `_.find` except that it returns the key of the first - * element `predicate` returns truthy for instead of the element itself. - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {string|undefined} Returns the key of the matched element, else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findKey(users, function(chr) { - * return chr.age < 40; - * }); - * // => 'barney' (iteration order is not guaranteed) - * - * // using the `_.matches` callback shorthand - * _.findKey(users, { 'age': 1, 'active': true }); - * // => 'pebbles' - * - * // using the `_.matchesProperty` callback shorthand - * _.findKey(users, 'active', false); - * // => 'fred' - * - * // using the `_.property` callback shorthand - * _.findKey(users, 'active'); - * // => 'barney' - */ - var findKey = createFindKey(baseForOwn); - - /** - * This method is like `_.findKey` except that it iterates over elements of - * a collection in the opposite order. - * - * If a property name is provided for `predicate` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `predicate` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to search. - * @param {Function|Object|string} [predicate=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {string|undefined} Returns the key of the matched element, else `undefined`. - * @example - * - * var users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * }; - * - * _.findLastKey(users, function(chr) { - * return chr.age < 40; - * }); - * // => returns `pebbles` assuming `_.findKey` returns `barney` - * - * // using the `_.matches` callback shorthand - * _.findLastKey(users, { 'age': 36, 'active': true }); - * // => 'barney' - * - * // using the `_.matchesProperty` callback shorthand - * _.findLastKey(users, 'active', false); - * // => 'fred' - * - * // using the `_.property` callback shorthand - * _.findLastKey(users, 'active'); - * // => 'pebbles' - */ - var findLastKey = createFindKey(baseForOwnRight); - - /** - * Iterates over own and inherited enumerable properties of an object invoking - * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked - * with three arguments: (value, key, object). Iteratee functions may exit - * iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns `object`. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forIn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed) - */ - var forIn = createForIn(baseFor); - - /** - * This method is like `_.forIn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns `object`. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forInRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c' - */ - var forInRight = createForIn(baseForRight); - - /** - * Iterates over own enumerable properties of an object invoking `iteratee` - * for each property. The `iteratee` is bound to `thisArg` and invoked with - * three arguments: (value, key, object). Iteratee functions may exit iteration - * early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns `object`. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwn(new Foo, function(value, key) { - * console.log(key); - * }); - * // => logs 'a' and 'b' (iteration order is not guaranteed) - */ - var forOwn = createForOwn(baseForOwn); - - /** - * This method is like `_.forOwn` except that it iterates over properties of - * `object` in the opposite order. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns `object`. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.forOwnRight(new Foo, function(value, key) { - * console.log(key); - * }); - * // => logs 'b' and 'a' assuming `_.forOwn` logs 'a' and 'b' - */ - var forOwnRight = createForOwn(baseForOwnRight); - - /** - * Creates an array of function property names from all enumerable properties, - * own and inherited, of `object`. - * - * @static - * @memberOf _ - * @alias methods - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the new array of property names. - * @example - * - * _.functions(_); - * // => ['after', 'ary', 'assign', ...] - */ - function functions(object) { - return baseFunctions(object, keysIn(object)); - } - - /** - * Gets the property value at `path` of `object`. If the resolved value is - * `undefined` the `defaultValue` is used in its place. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.get(object, 'a[0].b.c'); - * // => 3 - * - * _.get(object, ['a', '0', 'b', 'c']); - * // => 3 - * - * _.get(object, 'a.b.c', 'default'); - * // => 'default' - */ - function get(object, path, defaultValue) { - var result = object == null ? undefined : baseGet(object, toPath(path), path + ''); - return result === undefined ? defaultValue : result; - } - - /** - * Checks if `path` is a direct property. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` is a direct property, else `false`. - * @example - * - * var object = { 'a': { 'b': { 'c': 3 } } }; - * - * _.has(object, 'a'); - * // => true - * - * _.has(object, 'a.b.c'); - * // => true - * - * _.has(object, ['a', 'b', 'c']); - * // => true - */ - function has(object, path) { - if (object == null) { - return false; - } - var result = hasOwnProperty.call(object, path); - if (!result && !isKey(path)) { - path = toPath(path); - object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); - if (object == null) { - return false; - } - path = last(path); - result = hasOwnProperty.call(object, path); - } - return result || (isLength(object.length) && isIndex(path, object.length) && - (isArray(object) || isArguments(object))); - } - - /** - * Creates an object composed of the inverted keys and values of `object`. - * If `object` contains duplicate values, subsequent values overwrite property - * assignments of previous values unless `multiValue` is `true`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to invert. - * @param {boolean} [multiValue] Allow multiple values per key. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {Object} Returns the new inverted object. - * @example - * - * var object = { 'a': 1, 'b': 2, 'c': 1 }; - * - * _.invert(object); - * // => { '1': 'c', '2': 'b' } - * - * // with `multiValue` - * _.invert(object, true); - * // => { '1': ['a', 'c'], '2': ['b'] } - */ - function invert(object, multiValue, guard) { - if (guard && isIterateeCall(object, multiValue, guard)) { - multiValue = undefined; - } - var index = -1, - props = keys(object), - length = props.length, - result = {}; - - while (++index < length) { - var key = props[index], - value = object[key]; - - if (multiValue) { - if (hasOwnProperty.call(result, value)) { - result[value].push(key); - } else { - result[value] = [key]; - } - } - else { - result[value] = key; - } - } - return result; - } - - /** - * Creates an array of the own enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. See the - * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) - * for more details. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keys(new Foo); - * // => ['a', 'b'] (iteration order is not guaranteed) - * - * _.keys('hi'); - * // => ['0', '1'] - */ - var keys = !nativeKeys ? shimKeys : function(object) { - var Ctor = object == null ? undefined : object.constructor; - if ((typeof Ctor == 'function' && Ctor.prototype === object) || - (typeof object != 'function' && isArrayLike(object))) { - return shimKeys(object); - } - return isObject(object) ? nativeKeys(object) : []; - }; - - /** - * Creates an array of the own and inherited enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keysIn(new Foo); - * // => ['a', 'b', 'c'] (iteration order is not guaranteed) - */ - function keysIn(object) { - if (object == null) { - return []; - } - if (!isObject(object)) { - object = Object(object); - } - var length = object.length; - length = (length && isLength(length) && - (isArray(object) || isArguments(object)) && length) || 0; - - var Ctor = object.constructor, - index = -1, - isProto = typeof Ctor == 'function' && Ctor.prototype === object, - result = Array(length), - skipIndexes = length > 0; - - while (++index < length) { - result[index] = (index + ''); - } - for (var key in object) { - if (!(skipIndexes && isIndex(key, length)) && - !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { - result.push(key); - } - } - return result; - } - - /** - * The opposite of `_.mapValues`; this method creates an object with the - * same values as `object` and keys generated by running each own enumerable - * property of `object` through `iteratee`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the new mapped object. - * @example - * - * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { - * return key + value; - * }); - * // => { 'a1': 1, 'b2': 2 } - */ - var mapKeys = createObjectMapper(true); - - /** - * Creates an object with the same keys as `object` and values generated by - * running each own enumerable property of `object` through `iteratee`. The - * iteratee function is bound to `thisArg` and invoked with three arguments: - * (value, key, object). - * - * If a property name is provided for `iteratee` the created `_.property` - * style callback returns the property value of the given element. - * - * If a value is also provided for `thisArg` the created `_.matchesProperty` - * style callback returns `true` for elements that have a matching property - * value, else `false`. - * - * If an object is provided for `iteratee` the created `_.matches` style - * callback returns `true` for elements that have the properties of the given - * object, else `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function|Object|string} [iteratee=_.identity] The function invoked - * per iteration. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {Object} Returns the new mapped object. - * @example - * - * _.mapValues({ 'a': 1, 'b': 2 }, function(n) { - * return n * 3; - * }); - * // => { 'a': 3, 'b': 6 } - * - * var users = { - * 'fred': { 'user': 'fred', 'age': 40 }, - * 'pebbles': { 'user': 'pebbles', 'age': 1 } - * }; - * - * // using the `_.property` callback shorthand - * _.mapValues(users, 'age'); - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - */ - var mapValues = createObjectMapper(); - - /** - * The opposite of `_.pick`; this method creates an object composed of the - * own and inherited enumerable properties of `object` that are not omitted. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {Function|...(string|string[])} [predicate] The function invoked per - * iteration or property names to omit, specified as individual property - * names or arrays of property names. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'user': 'fred', 'age': 40 }; - * - * _.omit(object, 'age'); - * // => { 'user': 'fred' } - * - * _.omit(object, _.isNumber); - * // => { 'user': 'fred' } - */ - var omit = restParam(function(object, props) { - if (object == null) { - return {}; - } - if (typeof props[0] != 'function') { - var props = arrayMap(baseFlatten(props), String); - return pickByArray(object, baseDifference(keysIn(object), props)); - } - var predicate = bindCallback(props[0], props[1], 3); - return pickByCallback(object, function(value, key, object) { - return !predicate(value, key, object); - }); - }); - - /** - * Creates a two dimensional array of the key-value pairs for `object`, - * e.g. `[[key1, value1], [key2, value2]]`. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the new array of key-value pairs. - * @example - * - * _.pairs({ 'barney': 36, 'fred': 40 }); - * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed) - */ - function pairs(object) { - object = toObject(object); - - var index = -1, - props = keys(object), - length = props.length, - result = Array(length); - - while (++index < length) { - var key = props[index]; - result[index] = [key, object[key]]; - } - return result; - } - - /** - * Creates an object composed of the picked `object` properties. Property - * names may be specified as individual arguments or as arrays of property - * names. If `predicate` is provided it is invoked for each property of `object` - * picking the properties `predicate` returns truthy for. The predicate is - * bound to `thisArg` and invoked with three arguments: (value, key, object). - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The source object. - * @param {Function|...(string|string[])} [predicate] The function invoked per - * iteration or property names to pick, specified as individual property - * names or arrays of property names. - * @param {*} [thisArg] The `this` binding of `predicate`. - * @returns {Object} Returns the new object. - * @example - * - * var object = { 'user': 'fred', 'age': 40 }; - * - * _.pick(object, 'user'); - * // => { 'user': 'fred' } - * - * _.pick(object, _.isString); - * // => { 'user': 'fred' } - */ - var pick = restParam(function(object, props) { - if (object == null) { - return {}; - } - return typeof props[0] == 'function' - ? pickByCallback(object, bindCallback(props[0], props[1], 3)) - : pickByArray(object, baseFlatten(props)); - }); - - /** - * This method is like `_.get` except that if the resolved value is a function - * it is invoked with the `this` binding of its parent object and its result - * is returned. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to resolve. - * @param {*} [defaultValue] The value returned if the resolved value is `undefined`. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; - * - * _.result(object, 'a[0].b.c1'); - * // => 3 - * - * _.result(object, 'a[0].b.c2'); - * // => 4 - * - * _.result(object, 'a.b.c', 'default'); - * // => 'default' - * - * _.result(object, 'a.b.c', _.constant('default')); - * // => 'default' - */ - function result(object, path, defaultValue) { - var result = object == null ? undefined : object[path]; - if (result === undefined) { - if (object != null && !isKey(path, object)) { - path = toPath(path); - object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); - result = object == null ? undefined : object[last(path)]; - } - result = result === undefined ? defaultValue : result; - } - return isFunction(result) ? result.call(object) : result; - } - - /** - * Sets the property value of `path` on `object`. If a portion of `path` - * does not exist it is created. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to augment. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @returns {Object} Returns `object`. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.set(object, 'a[0].b.c', 4); - * console.log(object.a[0].b.c); - * // => 4 - * - * _.set(object, 'x[0].y.z', 5); - * console.log(object.x[0].y.z); - * // => 5 - */ - function set(object, path, value) { - if (object == null) { - return object; - } - var pathKey = (path + ''); - path = (object[pathKey] != null || isKey(path, object)) ? [pathKey] : toPath(path); - - var index = -1, - length = path.length, - lastIndex = length - 1, - nested = object; - - while (nested != null && ++index < length) { - var key = path[index]; - if (isObject(nested)) { - if (index == lastIndex) { - nested[key] = value; - } else if (nested[key] == null) { - nested[key] = isIndex(path[index + 1]) ? [] : {}; - } - } - nested = nested[key]; - } - return object; - } - - /** - * An alternative to `_.reduce`; this method transforms `object` to a new - * `accumulator` object which is the result of running each of its own enumerable - * properties through `iteratee`, with each invocation potentially mutating - * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked - * with four arguments: (accumulator, value, key, object). Iteratee functions - * may exit iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @category Object - * @param {Array|Object} object The object to iterate over. - * @param {Function} [iteratee=_.identity] The function invoked per iteration. - * @param {*} [accumulator] The custom accumulator value. - * @param {*} [thisArg] The `this` binding of `iteratee`. - * @returns {*} Returns the accumulated value. - * @example - * - * _.transform([2, 3, 4], function(result, n) { - * result.push(n *= n); - * return n % 2 == 0; - * }); - * // => [4, 9] - * - * _.transform({ 'a': 1, 'b': 2 }, function(result, n, key) { - * result[key] = n * 3; - * }); - * // => { 'a': 3, 'b': 6 } - */ - function transform(object, iteratee, accumulator, thisArg) { - var isArr = isArray(object) || isTypedArray(object); - iteratee = getCallback(iteratee, thisArg, 4); - - if (accumulator == null) { - if (isArr || isObject(object)) { - var Ctor = object.constructor; - if (isArr) { - accumulator = isArray(object) ? new Ctor : []; - } else { - accumulator = baseCreate(isFunction(Ctor) ? Ctor.prototype : undefined); - } - } else { - accumulator = {}; - } - } - (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) { - return iteratee(accumulator, value, index, object); - }); - return accumulator; - } - - /** - * Creates an array of the own enumerable property values of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.values(new Foo); - * // => [1, 2] (iteration order is not guaranteed) - * - * _.values('hi'); - * // => ['h', 'i'] - */ - function values(object) { - return baseValues(object, keys(object)); - } - - /** - * Creates an array of the own and inherited enumerable property values - * of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @static - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.valuesIn(new Foo); - * // => [1, 2, 3] (iteration order is not guaranteed) - */ - function valuesIn(object) { - return baseValues(object, keysIn(object)); - } - - /*------------------------------------------------------------------------*/ - - /** - * Checks if `n` is between `start` and up to but not including, `end`. If - * `end` is not specified it is set to `start` with `start` then set to `0`. - * - * @static - * @memberOf _ - * @category Number - * @param {number} n The number to check. - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @returns {boolean} Returns `true` if `n` is in the range, else `false`. - * @example - * - * _.inRange(3, 2, 4); - * // => true - * - * _.inRange(4, 8); - * // => true - * - * _.inRange(4, 2); - * // => false - * - * _.inRange(2, 2); - * // => false - * - * _.inRange(1.2, 2); - * // => true - * - * _.inRange(5.2, 4); - * // => false - */ - function inRange(value, start, end) { - start = +start || 0; - if (end === undefined) { - end = start; - start = 0; - } else { - end = +end || 0; - } - return value >= nativeMin(start, end) && value < nativeMax(start, end); - } - - /** - * Produces a random number between `min` and `max` (inclusive). If only one - * argument is provided a number between `0` and the given number is returned. - * If `floating` is `true`, or either `min` or `max` are floats, a floating-point - * number is returned instead of an integer. - * - * @static - * @memberOf _ - * @category Number - * @param {number} [min=0] The minimum possible value. - * @param {number} [max=1] The maximum possible value. - * @param {boolean} [floating] Specify returning a floating-point number. - * @returns {number} Returns the random number. - * @example - * - * _.random(0, 5); - * // => an integer between 0 and 5 - * - * _.random(5); - * // => also an integer between 0 and 5 - * - * _.random(5, true); - * // => a floating-point number between 0 and 5 - * - * _.random(1.2, 5.2); - * // => a floating-point number between 1.2 and 5.2 - */ - function random(min, max, floating) { - if (floating && isIterateeCall(min, max, floating)) { - max = floating = undefined; - } - var noMin = min == null, - noMax = max == null; - - if (floating == null) { - if (noMax && typeof min == 'boolean') { - floating = min; - min = 1; - } - else if (typeof max == 'boolean') { - floating = max; - noMax = true; - } - } - if (noMin && noMax) { - max = 1; - noMax = false; - } - min = +min || 0; - if (noMax) { - max = min; - min = 0; - } else { - max = +max || 0; - } - if (floating || min % 1 || max % 1) { - var rand = nativeRandom(); - return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand + '').length - 1)))), max); - } - return baseRandom(min, max); - } - - /*------------------------------------------------------------------------*/ - - /** - * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the camel cased string. - * @example - * - * _.camelCase('Foo Bar'); - * // => 'fooBar' - * - * _.camelCase('--foo-bar'); - * // => 'fooBar' - * - * _.camelCase('__foo_bar__'); - * // => 'fooBar' - */ - var camelCase = createCompounder(function(result, word, index) { - word = word.toLowerCase(); - return result + (index ? (word.charAt(0).toUpperCase() + word.slice(1)) : word); - }); - - /** - * Capitalizes the first character of `string`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to capitalize. - * @returns {string} Returns the capitalized string. - * @example - * - * _.capitalize('fred'); - * // => 'Fred' - */ - function capitalize(string) { - string = baseToString(string); - return string && (string.charAt(0).toUpperCase() + string.slice(1)); - } - - /** - * Deburrs `string` by converting [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) - * to basic latin letters and removing [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to deburr. - * @returns {string} Returns the deburred string. - * @example - * - * _.deburr('déjà vu'); - * // => 'deja vu' - */ - function deburr(string) { - string = baseToString(string); - return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, ''); - } - - /** - * Checks if `string` ends with the given target string. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to search. - * @param {string} [target] The string to search for. - * @param {number} [position=string.length] The position to search from. - * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`. - * @example - * - * _.endsWith('abc', 'c'); - * // => true - * - * _.endsWith('abc', 'b'); - * // => false - * - * _.endsWith('abc', 'b', 2); - * // => true - */ - function endsWith(string, target, position) { - string = baseToString(string); - target = (target + ''); - - var length = string.length; - position = position === undefined - ? length - : nativeMin(position < 0 ? 0 : (+position || 0), length); - - position -= target.length; - return position >= 0 && string.indexOf(target, position) == position; - } - - /** - * Converts the characters "&", "<", ">", '"', "'", and "\`", in `string` to - * their corresponding HTML entities. - * - * **Note:** No other characters are escaped. To escape additional characters - * use a third-party library like [_he_](https://mths.be/he). - * - * Though the ">" character is escaped for symmetry, characters like - * ">" and "/" don't need escaping in HTML and have no special meaning - * unless they're part of a tag or unquoted attribute value. - * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) - * (under "semi-related fun fact") for more details. - * - * Backticks are escaped because in Internet Explorer < 9, they can break out - * of attribute values or HTML comments. See [#59](https://html5sec.org/#59), - * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and - * [#133](https://html5sec.org/#133) of the [HTML5 Security Cheatsheet](https://html5sec.org/) - * for more details. - * - * When working with HTML you should always [quote attribute values](http://wonko.com/post/html-escaping) - * to reduce XSS vectors. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escape('fred, barney, & pebbles'); - * // => 'fred, barney, & pebbles' - */ - function escape(string) { - // Reset `lastIndex` because in IE < 9 `String#replace` does not. - string = baseToString(string); - return (string && reHasUnescapedHtml.test(string)) - ? string.replace(reUnescapedHtml, escapeHtmlChar) - : string; - } - - /** - * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", - * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escapeRegExp('[lodash](https://lodash.com/)'); - * // => '\[lodash\]\(https:\/\/lodash\.com\/\)' - */ - function escapeRegExp(string) { - string = baseToString(string); - return (string && reHasRegExpChars.test(string)) - ? string.replace(reRegExpChars, escapeRegExpChar) - : (string || '(?:)'); - } - - /** - * Converts `string` to [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the kebab cased string. - * @example - * - * _.kebabCase('Foo Bar'); - * // => 'foo-bar' - * - * _.kebabCase('fooBar'); - * // => 'foo-bar' - * - * _.kebabCase('__foo_bar__'); - * // => 'foo-bar' - */ - var kebabCase = createCompounder(function(result, word, index) { - return result + (index ? '-' : '') + word.toLowerCase(); - }); - - /** - * Pads `string` on the left and right sides if it's shorter than `length`. - * Padding characters are truncated if they can't be evenly divided by `length`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.pad('abc', 8); - * // => ' abc ' - * - * _.pad('abc', 8, '_-'); - * // => '_-abc_-_' - * - * _.pad('abc', 3); - * // => 'abc' - */ - function pad(string, length, chars) { - string = baseToString(string); - length = +length; - - var strLength = string.length; - if (strLength >= length || !nativeIsFinite(length)) { - return string; - } - var mid = (length - strLength) / 2, - leftLength = nativeFloor(mid), - rightLength = nativeCeil(mid); - - chars = createPadding('', rightLength, chars); - return chars.slice(0, leftLength) + string + chars; - } - - /** - * Pads `string` on the left side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padLeft('abc', 6); - * // => ' abc' - * - * _.padLeft('abc', 6, '_-'); - * // => '_-_abc' - * - * _.padLeft('abc', 3); - * // => 'abc' - */ - var padLeft = createPadDir(); - - /** - * Pads `string` on the right side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * _.padRight('abc', 6); - * // => 'abc ' - * - * _.padRight('abc', 6, '_-'); - * // => 'abc_-_' - * - * _.padRight('abc', 3); - * // => 'abc' - */ - var padRight = createPadDir(true); - - /** - * Converts `string` to an integer of the specified radix. If `radix` is - * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal, - * in which case a `radix` of `16` is used. - * - * **Note:** This method aligns with the [ES5 implementation](https://es5.github.io/#E) - * of `parseInt`. - * - * @static - * @memberOf _ - * @category String - * @param {string} string The string to convert. - * @param {number} [radix] The radix to interpret `value` by. - * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. - * @returns {number} Returns the converted integer. - * @example - * - * _.parseInt('08'); - * // => 8 - * - * _.map(['6', '08', '10'], _.parseInt); - * // => [6, 8, 10] - */ - function parseInt(string, radix, guard) { - // Firefox < 21 and Opera < 15 follow ES3 for `parseInt`. - // Chrome fails to trim leading whitespace characters. - // See https://code.google.com/p/v8/issues/detail?id=3109 for more details. - if (guard ? isIterateeCall(string, radix, guard) : radix == null) { - radix = 0; - } else if (radix) { - radix = +radix; - } - string = trim(string); - return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10)); - } - - /** - * Repeats the given string `n` times. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to repeat. - * @param {number} [n=0] The number of times to repeat the string. - * @returns {string} Returns the repeated string. - * @example - * - * _.repeat('*', 3); - * // => '***' - * - * _.repeat('abc', 2); - * // => 'abcabc' - * - * _.repeat('abc', 0); - * // => '' - */ - function repeat(string, n) { - var result = ''; - string = baseToString(string); - n = +n; - if (n < 1 || !string || !nativeIsFinite(n)) { - return result; - } - // Leverage the exponentiation by squaring algorithm for a faster repeat. - // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. - do { - if (n % 2) { - result += string; - } - n = nativeFloor(n / 2); - string += string; - } while (n); - - return result; - } - - /** - * Converts `string` to [snake case](https://en.wikipedia.org/wiki/Snake_case). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the snake cased string. - * @example - * - * _.snakeCase('Foo Bar'); - * // => 'foo_bar' - * - * _.snakeCase('fooBar'); - * // => 'foo_bar' - * - * _.snakeCase('--foo-bar'); - * // => 'foo_bar' - */ - var snakeCase = createCompounder(function(result, word, index) { - return result + (index ? '_' : '') + word.toLowerCase(); - }); - - /** - * Converts `string` to [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the start cased string. - * @example - * - * _.startCase('--foo-bar'); - * // => 'Foo Bar' - * - * _.startCase('fooBar'); - * // => 'Foo Bar' - * - * _.startCase('__foo_bar__'); - * // => 'Foo Bar' - */ - var startCase = createCompounder(function(result, word, index) { - return result + (index ? ' ' : '') + (word.charAt(0).toUpperCase() + word.slice(1)); - }); - - /** - * Checks if `string` starts with the given target string. - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The string to search. - * @param {string} [target] The string to search for. - * @param {number} [position=0] The position to search from. - * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`. - * @example - * - * _.startsWith('abc', 'a'); - * // => true - * - * _.startsWith('abc', 'b'); - * // => false - * - * _.startsWith('abc', 'b', 1); - * // => true - */ - function startsWith(string, target, position) { - string = baseToString(string); - position = position == null - ? 0 - : nativeMin(position < 0 ? 0 : (+position || 0), string.length); - - return string.lastIndexOf(target, position) == position; - } - - /** - * Creates a compiled template function that can interpolate data properties - * in "interpolate" delimiters, HTML-escape interpolated data properties in - * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data - * properties may be accessed as free variables in the template. If a setting - * object is provided it takes precedence over `_.templateSettings` values. - * - * **Note:** In the development build `_.template` utilizes - * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) - * for easier debugging. - * - * For more information on precompiling templates see - * [lodash's custom builds documentation](https://lodash.com/custom-builds). - * - * For more information on Chrome extension sandboxes see - * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). - * - * @static - * @memberOf _ - * @category String - * @param {string} [string=''] The template string. - * @param {Object} [options] The options object. - * @param {RegExp} [options.escape] The HTML "escape" delimiter. - * @param {RegExp} [options.evaluate] The "evaluate" delimiter. - * @param {Object} [options.imports] An object to import into the template as free variables. - * @param {RegExp} [options.interpolate] The "interpolate" delimiter. - * @param {string} [options.sourceURL] The sourceURL of the template's compiled source. - * @param {string} [options.variable] The data object variable name. - * @param- {Object} [otherOptions] Enables the legacy `options` param signature. - * @returns {Function} Returns the compiled template function. - * @example - * - * // using the "interpolate" delimiter to create a compiled template - * var compiled = _.template('hello <%= user %>!'); - * compiled({ 'user': 'fred' }); - * // => 'hello fred!' - * - * // using the HTML "escape" delimiter to escape data property values - * var compiled = _.template('<%- value %>'); - * compiled({ 'value': '