");
};
+ // helper function to wrap text - https://bl.ocks.org/mbostock/7555321
+ v.tools.wrapLabels = function(labels, width) {
+ labels.each(function(label, i) {
+ var text = d3.select(this);
+ if (i === 0) {
+ v.status.labelFontSize = parseInt(text.style("font-size"));
+ }
+ if (!this.hasAttribute("lines")) {
+ var words = text.text().split(/\s+/).reverse(),
+ word,
+ line = [],
+ lineNumber = 0,
+ lineHeight = v.status.labelFontSize * v.conf.wrappedLabelLineHeight,
+ x = text.attr("x"),
+ y = text.attr("y"),
+ dy = 0,
+ tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "px");
+
+ while (word = words.pop()) { // jshint ignore:line
+ line.push(word);
+ tspan.text(line.join(" "));
+ if (tspan.node().getComputedTextLength() > width) {
+ line.pop();
+ tspan.text(line.join(" "));
+ line = [word];
+ tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight +
+ dy + "px").text(word);
+ }
+ }
+ //save number of lines
+ text.attr("lines", lineNumber + 1);
+ }
+ });
+ };
+
/*******************************************************************************************************************
* LIBRARIES
*/
@@ -1963,7 +2159,7 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
};
- var intersect = function(x1, x2, x3, x4, y1, y2, y3, y4) {
+ var intersect = function(x1, x2, x3, x4, y1, y2, y3, y4) { // jshint ignore:line
// returns true if two lines intersect, else false
// from http://paulbourke.net/geometry/lineline2d/
@@ -2478,7 +2674,7 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
};
/* <-------------------------------------------------------- END MODIFICATION */
- function sign(x) {
+ function sign(x) { // jshint ignore:line
return x ? x < 0 ? -1 : 1 : 0;
}
@@ -2497,15 +2693,24 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
* PUBLIC GRAPH FUNCTION AND API METHODS
*/
- function graph() {}
-
// public start function: get data and start visualization
- graph.start = function(pData) {
+ /**
+ * This method starts the graph. You can configure your graph with all the available methods, but without the `start` method your changes will NOT take into effect.
+ *
+ * You can pass new data (see {@tutorial included-sample-data}) to the `start` method. Data can be a XML string, JSON string or JavaScript object (JSON). If you use the APEX plugin, then the `start` method internally does the AJAX call to your Oracle database, but you can prevent this behavior by passing data to this method.
+ *
+ * This also means, that you can use data from a textarea or a report for the APEX plugin, to overwrite the existing data and you do not need to configure any query to run this plugin. If you do so and you do not pass data to the `start` method on the very first call, then the plugin provides sample data - it is the same data with the [APEX online demo](https://apex.oracle.com/pls/apex/f?p=18290:1) of this plugin, there is no query configured and you get therefore the sampledata :-)
+ * @see {@link module:API.render}
+ * @see {@link module:API.resume}
+ * @param {(string|Object)} [data=Sample data EMP table flavoured] - Can be a XML string, JSON string or JavaScript object (JSON)
+ * @returns {Object} The graph object for method chaining.
+ */
+ graph.start = function(data) {
var firstChar;
// try to use the input data - this means also, we can overwrite the data from APEX with raw data (textarea or
// whatever you like...)
- if (pData) {
- graph.render(pData);
+ if (data) {
+ graph.render(data);
}
// if we have no data, then we try to use the APEX context (if APEX plugin ID is set)
else if (v.status.apexPluginId) {
@@ -2583,8 +2788,16 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
}
return graph;
};
-
- graph.render = function(pData) {
+ /**
+ * The `render` method does the same as the `start` method - the only difference is, that the `render` method does not try to load data, if you use the APEX plugin. You can use this method after changing options which need a `render` cycle to take the changes into effect:
+ *
+ * example.minNodeRadius(4).maxNodeRadius(20).render();
+ * @see {@link module:API.start}
+ * @see {@link module:API.resume}
+ * @param {(string|Object)} [data=Sample data EMP table flavoured] - Can be a XML string, JSON string or JavaScript object (JSON)
+ * @returns {Object} The graph object for method chaining.
+ */
+ graph.render = function(data) {
/* jshint -W074, -W071 */
var message;
v.status.graphStarted = true;
@@ -2593,35 +2806,35 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
v.tools.triggerApexEvent(document.querySelector("#" + v.dom.containerId), "apexbeforerefresh");
// if we start the rendering the first time and there is no input data, then provide sample data
- if (!pData && !v.status.graphReady) {
+ if (!data && !v.status.graphReady) {
v.tools.logError("Houston, we have a problem - we have to provide sample data.");
v.status.sampleData = true;
- pData = v.data.sampleData;
- } else if (pData) {
+ data = v.data.sampleData;
+ } else if (data) {
v.status.sampleData = false;
}
// if we have incoming data, than we do our transformations here, otherwise we use the existing data
- if (pData) {
+ if (data) {
if (v.status.graphReady) {
v.status.graphOldPositions = graph.positions();
}
// data is an object
- if (pData.constructor === Object) {
- v.data.dataConverted = pData;
+ if (data.constructor === Object) {
+ v.data.dataConverted = data;
if (v.conf.debug) {
v.tools.log("Data object:");
v.tools.log(v.data.dataConverted, true);
}
}
// data is a string
- else if (pData.constructor === String) {
+ else if (data.constructor === String) {
// convert incoming data depending on type
- if (pData.trim().substr(0, 1) === "<") {
+ if (data.trim().substr(0, 1) === "<") {
try {
- v.data.dataConverted = v.tools.xmlToJson(v.tools.parseXml(pData));
+ v.data.dataConverted = v.tools.xmlToJson(v.tools.parseXml(data));
if (v.data.dataConverted === null) {
message = "Unable to convert XML string.";
v.tools.logError(message);
@@ -2652,9 +2865,9 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
}
};
}
- } else if (pData.trim().substr(0, 1) === "{") {
+ } else if (data.trim().substr(0, 1) === "{") {
try {
- v.data.dataConverted = JSON.parse(pData);
+ v.data.dataConverted = JSON.parse(data);
} catch (e) {
message = "Unable to parse JSON string: " + e.message + ".";
v.tools.logError(message);
@@ -2687,7 +2900,7 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
}
if (v.conf.debug) {
v.tools.log("Data string:");
- v.tools.log(pData, true);
+ v.tools.log(data, true);
v.tools.log("Converted data object:");
v.tools.log(v.data.dataConverted, true);
}
@@ -2872,7 +3085,7 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
v.conf.positions = null;
v.status.graphOldPositions = null;
- } //END: if (pData)
+ } //END: if (data)
// set color and radius function and calculate nodes radius
v.tools.setColorFunction();
@@ -3121,6 +3334,7 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
.transform(v.conf.transform)
.autoRefresh(v.conf.autoRefresh)
.linkDistance(v.conf.linkDistance)
+ .wrapLabels(v.conf.wrapLabels)
.charge(v.conf.charge)
.chargeDistance(v.conf.chargeDistance)
.gravity(v.conf.gravity)
@@ -3148,12 +3362,27 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The `resume` method restarts only the force on your graph without a `render` cycle. This saves CPU time and can be useful if you change only things in your graph which do not need rendering to taking into effect:
+ *
+ * example.releaseFixedNodes().resume();
+ * @see {@link module:API.start}
+ * @see {@link module:API.render}
+ * @returns {Object} The graph object for method chaining.
+ */
graph.resume = function() {
v.main.force.resume();
v.tools.createCustomizeWizardIfNotRendering();
return graph;
};
+ /**
+ * If true, a class named border is added to the SVG element, if false the class will be removed. The border itself is defined in the delivered CSS - you can overwrite it if the current style does not match your needs. No `render` or `resume` call needed to take into effect:
+ *
+ * example.showBorder(false);
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.showBorder = function(value) {
if (!arguments.length) {
return v.conf.showBorder;
@@ -3166,6 +3395,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, a legend for all COLORVALUEs in the node data is rendered in the bottom left corner of the graph. No `render` or `resume` call needed to take into effect:
+ *
+ * example.showLegend(false);
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.showLegend = function(value) {
if (!arguments.length) {
return v.conf.showLegend;
@@ -3183,6 +3419,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, then links with the same source and target are rendered along a path around the node bottom. Needs a `render` call to take into effect:
+ *
+ * example.showSelfLinks(false).render();
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.showSelfLinks = function(value) {
if (!arguments.length) {
return v.conf.showSelfLinks;
@@ -3194,6 +3437,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, you get an marker at the end of a link. Needs a `render` call to take into effect:
+ *
+ * example.showLinkDirection(false).render();
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.showLinkDirection = function(value) {
if (!arguments.length) {
return v.conf.showLinkDirection;
@@ -3205,6 +3455,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true and you provided in your source data an attribute INFOSTRING, then a tooltip is shown by hovering a node. No `render` or `resume` call needed to take into effect:
+ *
+ * example.showTooltips(false);
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.showTooltips = function(value) {
if (!arguments.length) {
return v.conf.showTooltips;
@@ -3216,6 +3473,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The position where tooltips are shown in the graph - can be `node`, `svgTopLeft` or `svgTopRight`. No `render` or `resume` call needed to take into effect:
+ *
+ * example.tooltipPosition('node');
+ * @param {string} [value=svgTopRight] - - The new config value.
+ * @returns {(string|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.tooltipPosition = function(value) {
if (!arguments.length) {
return v.conf.tooltipPosition;
@@ -3227,6 +3491,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Color scheme can be `color20`, `color20b`, `color20c`, `color10` or `direct`. The first four use the color functions provided by D3, which return up to 20 colors for the given keywords for your data attribute COLORVALUE - this can be a text like a department name or a postal zip code. With the last option you can provide direct css color values in your data like blue or #123456. No `render` or `resume` call needed to take into effect:
+ *
+ * example.colorScheme('color10');
+ * @param {string} [value=color20] - The new config value.
+ * @returns {(string|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.colorScheme = function(value) {
if (!arguments.length) {
return v.conf.colorScheme;
@@ -3248,6 +3519,16 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true and you provided in your node data an attribute LABEL, then a label is rendered on top of the node. Needs a `render` call to take into effect:
+ *
+ * example.showLabels(false).render();
+ * @see {@link module:API.wrapLabels}
+ * @see {@link module:API.wrappedLabelWidth}
+ * @see {@link module:API.wrappedLabelLineHeight}
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.showLabels = function(value) {
if (!arguments.length) {
return v.conf.showLabels;
@@ -3259,6 +3540,99 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true long labels are wrapped. Needs a `render` call to take into effect:
+ *
+ * example.wrapLabels(true).render();
+ * @see {@link module:API.showLabels}
+ * @see {@link module:API.wrappedLabelWidth}
+ * @see {@link module:API.wrappedLabelLineHeight}
+ * @see {@link module:API.labelsCircular}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
+ graph.wrapLabels = function(value) {
+ if (!arguments.length) {
+ return v.conf.wrapLabels;
+ }
+ v.conf.wrapLabels = value;
+ if (v.conf.wrapLabels) {
+ v.status.wrapLabelsOnNextTick = true;
+ }
+ if (v.status.graphStarted) {
+ v.main.labels.attr("lines", null);
+ v.tools.createCustomizeWizardIfNotRendering();
+ }
+ return graph;
+ };
+
+ /**
+ * The width of the labels, if option `wrapLabels` is set to true. Needs a `render` call to take into effect:
+ *
+ * example.wrappedLabelWidth(40).render();
+ * @see {@link module:API.showLabels}
+ * @see {@link module:API.wrapLabels}
+ * @see {@link module:API.wrappedLabelLineHeight}
+ * @see {@link module:API.labelsCircular}
+ * @param {number} [value=80] - The new config value.
+ * @returns {(number|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
+ graph.wrappedLabelWidth = function(value) {
+ if (!arguments.length) {
+ return v.conf.wrappedLabelWidth;
+ }
+ v.conf.wrappedLabelWidth = value;
+ if (v.conf.wrapLabels) {
+ v.main.labels.attr("lines", null);
+ v.status.wrapLabelsOnNextTick = true;
+ }
+ if (v.status.graphStarted) {
+ v.tools.createCustomizeWizardIfNotRendering();
+ }
+ return graph;
+ };
+
+ /**
+ * The line height of labels in `em`, if option `wrapLabels` is set to true. Needs a `render` call to take into effect:
+ *
+ * example.wrappedLabelLineHeight(1.5).render();
+ * @see {@link module:API.showLabels}
+ * @see {@link module:API.wrapLabels}
+ * @see {@link module:API.wrappedLabelWidth}
+ * @see {@link module:API.labelsCircular}
+ * @param {number} [value=1.2] - The new config value.
+ * @returns {(number|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
+ graph.wrappedLabelLineHeight = function(value) {
+ if (!arguments.length) {
+ return v.conf.wrappedLabelLineHeight;
+ }
+ v.conf.wrappedLabelLineHeight = value;
+ if (v.conf.wrapLabels) {
+ v.status.wrapLabelsOnNextTick = true;
+ }
+ if (v.status.graphStarted) {
+ v.main.labels.attr("lines", null);
+ v.tools.createCustomizeWizardIfNotRendering();
+ }
+ return graph;
+ };
+
+ /**
+ * If true, then the labels are rendered along a path around the nodes.
+ *
+ * You can overwrite this attribute on node level by setting a attribute called LABELCIRCULAR on the node to true or false. As an example you can see this in the online demo on the node named KING.
+ *
+ * ATTENTION: If you set the LABELCIRCULAR attribute on a specific or all nodes, then the global configuration parameter labelsCircular has no effect on these nodes.
+ *
+ * Needs a `render` call to take into effect:
+ *
+ * example.labelsCircular(true).render();
+ * @see {@link module:API.labelDistance}
+ * @see {@link module:API.wrapLabels}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.labelsCircular = function(value) {
if (!arguments.length) {
return v.conf.labelsCircular;
@@ -3270,6 +3644,34 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The distance of a label from the nodes outer border. Needs a `render` call to take into effect:
+ *
+ * example.labelDistance(18).render();
+ * @see {@link module:API.labelsCircular}
+ * @see {@link module:API.wrapLabels}
+ * @param {number} [value=12] - The new config value.
+ * @returns {(number|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
+ graph.labelDistance = function(value) {
+ if (!arguments.length) {
+ return v.conf.labelDistance;
+ }
+ v.conf.labelDistance = value;
+ if (v.status.graphStarted) {
+ v.tools.createCustomizeWizardIfNotRendering();
+ }
+ return graph;
+ };
+
+ /**
+ * If set to true the labels are aligned with a simulated annealing function to prevent overlapping when the graph is cooled down (correctly on the force end event and only on labels, who are not circular). Needs a `resume` call to take into effect:
+ *
+ * example.preventLabelOverlappingOnForceEnd(true).render();
+ * @see {@link module:API.labelPlacementIterations}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.preventLabelOverlappingOnForceEnd = function(value) {
if (!arguments.length) {
return v.conf.preventLabelOverlappingOnForceEnd;
@@ -3281,6 +3683,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The number of iterations for the preventLabelOverlappingOnForceEnd option - default is 250 - as higher the number, as higher the quality of the result. For details refer to the [description of the simulated annealing function of the author Evan Wang](https://github.com/tinker10/D3-Labeler). Needs a `resume` call to take into effect:
+ *
+ * example.preventLabelOverlappingOnForceEnd(true).resume();
+ * @see {@link module:API.labelPlacementIterations}
+ * @param {number} [value=250] - The new config value.
+ * @returns {(number|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.labelPlacementIterations = function(value) {
if (!arguments.length) {
return v.conf.labelPlacementIterations;
@@ -3292,6 +3702,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, the nodes are draggable. No `render` or `resume` call needed to take into effect:
+ *
+ * example.dragMode(false);
+ * @see {@link module:API.pinMode}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.dragMode = function(value) {
if (!arguments.length) {
return v.conf.dragMode;
@@ -3310,6 +3728,15 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, the nodes are fixed (pinned) at the end of a drag event. No `render` or `resume` call needed to take into effect:
+ *
+ * example.pinMode(true);
+ * @see {@link module:API.releaseFixedNodes}
+ * @see {@link module:API.dragMode}
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.pinMode = function(value) {
if (!arguments.length) {
return v.conf.pinMode;
@@ -3328,6 +3755,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, you can select miltiple nodes with a lasso - think of a graphical multiselect :-). No `render` or `resume` call needed to take into effect:
+ *
+ * example.lassoMode(true);
+ * @see {@link module:API.zoomMode}
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.lassoMode = function(value) {
if (!arguments.length) {
return v.conf.lassoMode;
@@ -3383,28 +3818,22 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
- graph.transform = function(value) {
- if (!arguments.length) {
- return {
- "translate": v.main.zoom.translate(),
- "scale": v.main.zoom.scale()
- };
- } else {
- v.main.zoom.translate(value.translate);
- v.main.zoom.scale(value.scale);
- v.conf.transform = {
- "translate": v.main.zoom.translate(),
- "scale": v.main.zoom.scale()
- };
- if (v.conf.zoomMode && v.status.graphStarted) {
- v.dom.graph.attr("transform", "translate(" + v.main.zoom.translate() + ")scale(" +
- v.main.zoom.scale() + ")");
- v.tools.writeConfObjectIntoWizard();
- }
- }
- return graph;
- };
-
+ /**
+ * If true, you can zoom and pan the graph.
+ *
+ * ATTENTION: When zoomMode is set to true then the lassoMode is only working with the pressed alt or shift key.
+ *
+ * KNOWN BUG: In iOS it is after the first zoom event no more possible to drag a node - instead the whole graph is moved - this is, because iOS Safari provide a wrong event.target.tagName. Also a problem: your are not able to press the alt or shift key - if you want to use lasso and zoom together on a touch device, you have to provide a workaround. One possible way is to provide a button, which turns zoom mode on and off with the API zoomMode method - then the user has the choice between these two modes - not comfortable, but working.
+ *
+ * No `render` or `resume` call needed to take into effect:
+ *
+ * example.zoomMode(true);
+ * @see {@link module:API.zoom}
+ * @see {@link module:API.zoomSmooth}
+ * @see {@link module:API.transform}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.zoomMode = function(value) {
if (!arguments.length) {
return v.conf.zoomMode;
@@ -3412,20 +3841,10 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
v.conf.zoomMode = value;
if (v.status.graphStarted) {
if (v.conf.zoomMode) {
- v.dom.graphOverlay.call(v.main.zoom);
v.main.zoom.scaleExtent([v.conf.minZoomFactor, v.conf.maxZoomFactor])
.size([v.tools.getGraphWidth(), v.conf.height])
- .on("zoom", function() {
- v.main.zoom.translate(d3.event.translate);
- v.main.zoom.scale(d3.event.scale);
- v.conf.transform = {
- "translate": v.main.zoom.translate(),
- "scale": v.main.zoom.scale()
- };
- v.dom.graph.attr("transform", "translate(" + v.main.zoom.translate() + ")scale(" +
- v.main.zoom.scale() + ")");
- v.tools.writeConfObjectIntoWizard();
- });
+ .on("zoom", v.main.zoomed);
+ v.dom.graphOverlay.call(v.main.zoom);
// save zoom events for use in event proxy
v.events.dblclickZoom = v.dom.graphOverlay.on("dblclick.zoom");
v.events.mousedownZoom = v.dom.graphOverlay.on("mousedown.zoom");
@@ -3464,6 +3883,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * No `render` or `resume` call needed to take into effect::
+ *
+ * example.minZoomFactor(0.1);
+ * @see {@link module:API.maxZoomFactor}
+ * @param {number} [value=0.2] - The new config value.
+ * @returns {(number|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.minZoomFactor = function(value) {
if (!arguments.length) {
return v.conf.minZoomFactor;
@@ -3475,6 +3902,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * No `render` or `resume` call needed to take into effect::
+ *
+ * example.maxZoomFactor(10);
+ * @see {@link module:API.minZoomFactor}
+ * @param {number} [value=5] - The new config value.
+ * @returns {(number|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.maxZoomFactor = function(value) {
if (!arguments.length) {
return v.conf.maxZoomFactor;
@@ -3486,75 +3921,150 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If the graph option `zoomMode` is set to true, then the graph is centered to the given position and scaled to the calculated scale factor (effective graph with / viewportWidth). The reason to have a viewportWidth instead of a scale factor is, that you can rely on given data like the coordinates and radius of a node without calculating the scale factor by yourself - you define your desired viewport width and the zoom method is calculating the neccesary scale factor for this viewport width. If the calculated scale factor is less then or greater then the configured minimum and maximum scale factors, then these configured scale factors are used. The reason for this a good user experience, since the graph would be otherwise falling back on these scale factors when the user is scaling the graph by mouse or touch events. No `render` or `resume` call needed to take into effect:
+ *
+ * var node = example.nodeDataById('9999');
+ * example.zoom(node.x, node.y, node.radius * 6);
+ * @see {@link module:API.zoomMode}
+ * @see {@link module:API.zoomSmooth}
+ * @see {@link module:API.minZoomFactor}
+ * @see {@link module:API.maxZoomFactor}
+ * @see {@link module:API.transform}
+ * @param {number} [centerX=graph width / 2] - The horizontal center position.
+ * @param {number} [centerY=graph height / 2] - The vertical center position.
+ * @param {number} [viewportWidth=graph width] - The desired viewport width.
+ * @returns {Object} The graph object for method chaining.
+ */
graph.zoom = function(centerX, centerY, viewportWidth) {
- if (v.conf.zoomMode) {
- var width = v.tools.getGraphWidth();
- if (!centerX) {
- centerX = width / 2;
- }
- if (!centerY) {
- centerY = v.conf.height / 2;
- }
- if (!viewportWidth) {
- viewportWidth = width;
- }
- v.main.zoom.scale(width / viewportWidth);
- if (v.main.zoom.scale() < v.conf.minZoomFactor) {
- v.main.zoom.scale(v.conf.minZoomFactor);
- }
- if (v.main.zoom.scale() > v.conf.maxZoomFactor) {
- v.main.zoom.scale(v.conf.maxZoomFactor);
- }
- v.main.zoom.translate([
- (width / 2 - centerX * v.main.zoom.scale()), (v.conf.height / 2 - centerY * v.main.zoom.scale())
- ]);
- v.conf.transform = {
- "translate": v.main.zoom.translate(),
- "scale": v.main.zoom.scale()
- };
- v.main.zoom.event(v.dom.graphOverlay);
- v.tools.writeConfObjectIntoWizard();
- }
+ graph.zoomSmooth(centerX, centerY, viewportWidth, 0);
return graph;
};
+ /**
+ * This method does the same as the zoom method - the difference is, that the zoom is animated in a nice way and there is a optional fourth parameter for the duration of the transition, which defaults to 1500ms. No `render` or `resume` call needed to take into effect:
+ *
+ * var node = example.nodeDataById('8888');
+ * example.zoomSmooth(node.x, node.y, node.radius * 6); // default duration of 1500ms
+ *
+ * var node = example.nodeDataById('9999');
+ * example.zoomSmooth(node.x, node.y, node.radius * 6, 3000); // duration of 3000ms
+ * @see {@link module:API.zoomMode}
+ * @see {@link module:API.zoom}
+ * @see {@link module:API.minZoomFactor}
+ * @see {@link module:API.maxZoomFactor}
+ * @see {@link module:API.transform}
+ * @param {number} [centerX=graph width / 2] - The horizontal center position.
+ * @param {number} [centerY=graph height / 2] - The vertical center position.
+ * @param {number} [viewportWidth=graph width] - The desired viewport width.
+ * @param {number} [duration=1500] - the duration of the transition
+ * @returns {Object} The graph object for method chaining.
+ */
graph.zoomSmooth = function(centerX, centerY, viewportWidth, duration) {
- if (v.conf.zoomMode) {
- var width = v.tools.getGraphWidth();
- if (!centerX) {
- centerX = width / 2;
- }
- if (!centerY) {
- centerY = v.conf.height / 2;
- }
- if (!viewportWidth) {
- viewportWidth = width;
- }
- if (!duration) {
- duration = 1500;
- }
- v.main.zoom.scale(width / viewportWidth);
- if (v.main.zoom.scale() < v.conf.minZoomFactor) {
- v.main.zoom.scale(v.conf.minZoomFactor);
- }
- if (v.main.zoom.scale() > v.conf.maxZoomFactor) {
- v.main.zoom.scale(v.conf.maxZoomFactor);
- }
- v.main.zoom.translate([
- (width / 2 - centerX * v.main.zoom.scale()), (v.conf.height / 2 - centerY * v.main.zoom.scale())
- ]);
- v.conf.translate = {
+ // http://bl.ocks.org/linssen/7352810
+ var x, y, scale;
+ var width = v.tools.getGraphWidth(); // could be different then configured (responsive)
+ centerX = (isNaN(centerX) ? width / 2 : parseInt(centerX));
+ centerY = (isNaN(centerY) ? v.conf.height / 2 : parseInt(centerY));
+ viewportWidth = (isNaN(viewportWidth) ? width : parseInt(viewportWidth));
+ duration = (isNaN(duration) ? 1500 : parseInt(duration));
+ scale = width / viewportWidth;
+ x = width / 2 - centerX * scale;
+ y = v.conf.height / 2 - centerY * scale;
+ v.main.interpolateZoom([x, y], scale, duration);
+ return graph;
+ };
+
+ /**
+ * Behaves like a normal getter/setter (the `zoom` and `zoomSmooth` methods implements only setters) and can be used in the conf object to initialize the graph with different translate values/scale factors than [0,0]/1. Works only, if the `zoomMode` is set to true. The current transform value(an object) is rendered in the customization wizard conf object text area like all other options when the current value is different then the default value. No `render` or `resume` call needed to take into effect:
+ *
+ * //example.zoomMode(true);
+ * example.transform({"translate":[100,100],"scale":0.5});
+ * @see {@link module:API.zoomMode}
+ * @see {@link module:API.zoom}
+ * @see {@link module:API.zoomSmooth}
+ * @param {Object} [transform={“translate”:[0,0],“scale”:1}] - The new config value.
+ * @returns {Object} The current config value if no parameter is given or the graph object for method chaining.
+ */
+ graph.transform = function(transform) {
+ if (!arguments.length) {
+ return {
"translate": v.main.zoom.translate(),
"scale": v.main.zoom.scale()
};
- v.dom.graphOverlay.transition()
- .duration(duration)
- .call(v.main.zoom.event);
- v.tools.writeConfObjectIntoWizard();
+ } else {
+ v.main.interpolateZoom(transform.translate, transform.scale, 0);
}
return graph;
};
+ /**
+ * Helper/Command method - automatically zoom, so that the whole graph is visible and optimal sized. No `render` or `resume` call needed to take into effect:
+ *
+ * example.zoomToFit();
+ * @see {@link module:API.zoomMode}
+ * @see {@link module:API.zoomSmooth}
+ * @see {@link module:API.minZoomFactor}
+ * @see {@link module:API.maxZoomFactor}
+ * @see {@link module:API.transform}
+ * @see {@link module:API.zoomToFitOnForceEnd}
+ * @param {number} [duration=500] - The transition duration in milliseconds.
+ * @returns {Object} The graph object for method chaining.
+ */
+ graph.zoomToFit = function(duration) {
+ var svg = {},
+ graph_, padding = 10,
+ x, y, scale;
+ duration = (isNaN(duration) ? 500 : parseInt(duration));
+ svg.width = v.tools.getGraphWidth();
+ svg.height = v.conf.height;
+ graph_ = v.dom.graph.node().getBBox();
+ scale = Math.min((svg.height - 2 * padding) / graph_.height,
+ (svg.width - 2 * padding) / graph_.width);
+ x = (svg.width - graph_.width * scale) / 2 - graph_.x * scale;
+ y = (svg.height - graph_.height * scale) / 2 - graph_.y * scale;
+ v.main.interpolateZoom([x, y], scale, duration);
+ return graph;
+ };
+
+ /**
+ * Automatically zoom at force end, so that the whole graph is visible and optimal sized. Needs a `resume` call to take into effect or must be set at initialization (before the graph starts). If enabled it fires at every force end event. If you only want to resize your graph once than have a look at the command/helper method `zoomToFit`:
+ *
+ * //running graph: change config
+ * example.zoomToFitOnForceEnd(true).resume();
+ * //alternative way without a resume call
+ * example.zoomToFitOnForceEnd(true).zoomToFit();
+ *
+ * //running graph: resize only once
+ * example.zoomToFit();
+ * @see {@link module:API.zoomMode}
+ * @see {@link module:API.zoomSmooth}
+ * @see {@link module:API.minZoomFactor}
+ * @see {@link module:API.maxZoomFactor}
+ * @see {@link module:API.transform}
+ * @see {@link module:API.zoomToFit}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
+ graph.zoomToFitOnForceEnd = function(value) {
+ if (!arguments.length) {
+ return v.conf.zoomToFitOnForceEnd;
+ }
+ v.conf.zoomToFitOnForceEnd = value;
+ if (v.status.graphStarted) {
+ v.tools.createCustomizeWizardIfNotRendering();
+ }
+ return graph;
+ };
+
+ /**
+ * If true, a loading indicator is shown when used as a APEX plugin during the AJAX calls. If you want to show the loading indicator in a standalone implementation you can show and hide the loading indicator directly with the API method `showLoadingIndicator`:
+ *
+ * example.showLoadingIndicatorOnAjaxCall(false);
+ * @see {@link module:API.showLoadingIndicator}
+ * @param {boolean} [value=true] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.showLoadingIndicatorOnAjaxCall = function(value) {
if (!arguments.length) {
return v.conf.showLoadingIndicatorOnAjaxCall;
@@ -3563,6 +4073,18 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Helper method to directly show or hide a loading indicator. The APEX plugin do this implicitly on AJAX calls when the option `showLoadingIndicatorOnAjaxCall` is set to true. No `render` or `resume` call needed to take into effect:
+ *
+ * // Show:
+ * example.showLoadingIndicator(true);
+ *
+ * // Hide:
+ * example.showLoadingIndicator(false);
+ * @see {@link module:API.showLoadingIndicatorOnAjaxCall}
+ * @param {boolean} - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.showLoadingIndicator = function(value) {
if (v.tools.parseBool(value)) {
v.dom.loading.style("display", "block");
@@ -3572,6 +4094,15 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, fixed nodes are aligned to the nearest grid position on the drag end event. You can pin nodes, when `pinMode` is set to true or by delivering nodes with the attribute “fixed” set to true and “x” and “y” attributes for the position. If you have already fixed nodes on your graph you can also set this attribute at runtime and resume the force. Needs a `resume` call to take into effect:
+ *
+ * example.alignFixedNodesToGrid(true).resume();
+ * @see {@link module:API.gridSize}
+ * @see {@link module:API.pinMode}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.alignFixedNodesToGrid = function(value) {
if (!arguments.length) {
return v.conf.alignFixedNodesToGrid;
@@ -3601,6 +4132,38 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The grid size of the virtual grid for the option `alignFixedNodesToGrid`. Needs a `resume` call to take into effect:
+ *
+ * example.gridSize(100).alignFixedNodesToGrid(true).resume();
+ * @see {@link module:API.alignFixedNodesToGrid}
+ * @see {@link module:API.pinMode}
+ * @param {number} [value=50] - The new config value.
+ * @returns {(number|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
+ graph.gridSize = function(value) {
+ if (!arguments.length) {
+ return v.conf.gridSize;
+ }
+ v.conf.gridSize = value;
+ if (v.status.graphStarted) {
+ v.tools.createCustomizeWizardIfNotRendering();
+ }
+ return graph;
+ };
+
+ /**
+ * Command method (has no get or set function). Moves all fixed nodes in the provided direction. Needs a `resume` call to take into effect:
+ *
+ * example.moveFixedNodes(10,-5).resume();
+ *
+ * The example adds 10 to x position and -5 to y position to all fixed nodes. ATTENTION: If alignFixedNodesToGrid is set to true this can have unexpected behavior - you must then provide values greater then gridSize halved to see any changes on your graph, otherwise the positions are falling back to the nearest (current) grid position.
+ * @see {@link module:API.pinMode}
+ * @see {@link module:API.alignFixedNodesToGrid}
+ * @param {number} [x=0] - x value - positive or negative
+ * @param {number} [y=0] - y value - positive or negative
+ * @returns {Object} The graph object for method chaining.
+ */
graph.moveFixedNodes = function(x, y) {
if (v.status.graphStarted) {
if (!x) {
@@ -3623,17 +4186,31 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
- graph.gridSize = function(value) {
- if (!arguments.length) {
- return v.conf.gridSize;
- }
- v.conf.gridSize = value;
+ /**
+ * Command method (has no get or set function and expects no parameter): Release all fixed (pinned) nodes. Needs a `resume` call to take into effect:
+ *
+ * example.releaseFixedNodes().resume();
+ * @see {@link module:API.pinMode}
+ * @see {@link module:API.alignFixedNodesToGrid}
+ * @returns {Object} The graph object for method chaining.
+ */
+ graph.releaseFixedNodes = function() {
if (v.status.graphStarted) {
- v.tools.createCustomizeWizardIfNotRendering();
+ v.main.nodes.each(function(n) {
+ n.fixed = 0;
+ });
}
return graph;
};
+ /**
+ * Can be “none”, “click”, “dblclick” and “contextmenu” and defines, which event will release a node. This releasing of a node is sometimes a bit unstable (not on the code side, but on the visualizing side) and depends on the next tick event. You have to play around with this. If you want only release all nodes you can simply call the releaseFixedNodes method and resume the graph. No `render` or `resume` call needed to take into effect:
+ *
+ * example.nodeEventToStopPinMode("contextmenu");
+ * @see {@link module:API.releaseFixedNodes}
+ * @param {string} [value="contextmenu"] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.nodeEventToStopPinMode = function(value) {
if (!arguments.length) {
return v.conf.nodeEventToStopPinMode;
@@ -3645,6 +4222,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, the context menu default browser action on the nodes are prevented. This could be useful, if you want to implement an own context menu for the nodes. xxx:
+ *
+ * example.onNodeContextmenuPreventDefault(true);
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.onNodeContextmenuPreventDefault = function(value) {
if (!arguments.length) {
return v.conf.onNodeContextmenuPreventDefault;
@@ -3656,6 +4240,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Can be “none”, “click”, “dblclick” or “contextmenu”. Works only for nodes with a non empty LINK attribute. No `render` or `resume` call needed to take into effect:
+ *
+ * example.nodeEventToOpenLink("click");
+ * @param {string} [value="dblclick"] - The new config value.
+ * @returns {(string|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.nodeEventToOpenLink = function(value) {
if (!arguments.length) {
return v.conf.nodeEventToOpenLink;
@@ -3667,6 +4258,18 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * This text is used as the link target, when a node has a LINK attribute.
+ *
+ * There are three special keywords: “none”, “nodeID” and “domContainerID”. If you use “none”, the link is opened in the same window/tab where your graph is currently shown. If you use “nodeID”, the ID of the currently clicked node is used as the target attribute, this means - you get one window/tab for each node in your graph - when you click a second time on the same node, the window/tab is reused. The same with the keyword “domContainerID” - you get one window/tab for each graph on your page - when you click a second time on the same node, the window/tab is reused.
+ *
+ * Anything else is not interpreted - your given text is simply used as the target attribute of the link. This is also the case for the second option in the customize wizard called “_blank”. If you use this, then each click on a node opens in a new window/tab. You are not restricted to use only the predefined select options. It is up to you to overwrite the value in your configuration object. As an example: If you want to have always the same window/tab for each click on a node, then simply provide a text here, that fit your needs e.g. “myOwnWindowName”.
+ *
+ * example.nodeLinkTarget("myOwnWindowName");
+ * @see {@link module:API.nodeEventToOpenLink}
+ * @param {string} [value="_blank"] - The new config value.
+ * @returns {(string|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.nodeLinkTarget = function(value) {
if (!arguments.length) {
return v.conf.nodeLinkTarget;
@@ -3678,6 +4281,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, the graph is refreshed automatically. This makes only sense when running as APEX plugin - here you have the SQL queries for loading new data with AJAX. If you run your code standalone, you have to provide new data as a parameter in the start or render method and therefore you have to use your own auto refresh logic. No `render` or `resume` call needed to take into effect:
+ *
+ * example.autoRefresh(true);
+ * @see {@link module:API.refreshInterval}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.autoRefresh = function(value) {
if (!arguments.length) {
return v.conf.autoRefresh;
@@ -3699,6 +4310,18 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The refresh interval in milliseconds. No `render` or `resume` call needed to take into effect, but after changing the interval value you have to stop a current activated auto refresh and start it again to take the new value into effect:
+ *
+ * // only set the value and start auto refresh
+ * example.refreshInterval(4000).autoRefresh(true);
+ *
+ * // restart running auto refresh
+ * example.refreshInterval(2000).autoRefresh(false).autoRefresh(true);
+ * @see {@link module:API.autoRefresh}
+ * @param {number} [value=5000] - The new config value.
+ * @returns {(number|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.refreshInterval = function(value) {
if (!arguments.length) {
return v.conf.refreshInterval;
@@ -3710,6 +4333,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, the width of the chart(SVG element) is aligned to its DOM parent element. Needs a `render` call to take into effect:
+ *
+ * example.useDomParentWidth(true).render();
+ * @see {@link module:API.setDomParentPaddingToZero}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.useDomParentWidth = function(value) {
if (!arguments.length) {
return v.conf.useDomParentWidth;
@@ -3734,6 +4365,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * If true, the parent DOM element of the graph gets the style { padding: 0px; }. If set to false, this style is removed from the DOM parent of the graph. No `render` or `resume` call needed to take into effect:
+ *
+ * example.setDomParentPaddingToZero(true);
+ * @see {@link module:API.useDomParentWidth}
+ * @param {boolean} [value=false] - The new config value.
+ * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
+ */
graph.setDomParentPaddingToZero = function(value) {
if (!arguments.length) {
return v.conf.setDomParentPaddingToZero;
@@ -3750,10 +4389,26 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Returns the current with of the graphs DOM parent. This method expects no parameter and terminates the method chain.
+ *
+ * If the option useDomParentWidth is set to true, then this is the effective width of the graph - independent of the configured width.
+ *
+ * example.domParentWidth();
+ * @returns {number} The current DOM parent width.
+ */
graph.domParentWidth = function() {
return v.dom.containerWidth || v.tools.getSvgParentInnerWidth();
};
+ /**
+ * The width of the chart. Needs a `resume` call to take into effect:
+ *
+ * example.width(800).resume();
+ * @see {@link module:API.height}
+ * @param {number} [value=500] - The new chart width value.
+ * @returns {(number|Object)} The current chart width value if no parameter is given or the graph object for method chaining.
+ */
graph.width = function(value) {
if (!arguments.length) {
return v.conf.width;
@@ -3773,6 +4428,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The height of the chart. Needs a `resume` call to take into effect:
+ *
+ * example.height(600).resume();
+ * @see {@link module:API.width}
+ * @param {number} [value=500] - The new chart height value.
+ * @returns {(number|Object)} The current chart height value if no parameter is given or the graph object for method chaining.
+ */
graph.height = function(value) {
if (!arguments.length) {
return v.conf.height;
@@ -3796,6 +4459,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The minimum node radius. Each node radius is calculated by its SIZEVALUE attribute in a range between the minimum and the maximum node radius. Needs a `render` call to take into effect:
+ *
+ * example.minNodeRadius(2).render();
+ * @see {@link module:API.maxNodeRadius}
+ * @param {number} [value=6] - The new min node radius value.
+ * @returns {(number|Object)} The current min node radius value if no parameter is given or the graph object for method chaining.
+ */
graph.minNodeRadius = function(value) {
if (!arguments.length) {
return v.conf.minNodeRadius;
@@ -3814,6 +4485,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The maximum node radius. Each node radius is calculated by its SIZEVALUE attribute in a range between the minimum and the maximum node radius. Needs a `render` call to take into effect:
+ *
+ * example.maxNodeRadius(24).render();
+ * @see {@link module:API.minNodeRadius}
+ * @param {number} [value=18] - The new max node radius value.
+ * @returns {(number|Object)} The current max node radius value if no parameter is given or the graph object for method chaining.
+ */
graph.maxNodeRadius = function(value) {
if (!arguments.length) {
return v.conf.maxNodeRadius;
@@ -3832,17 +4511,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
- graph.labelDistance = function(value) {
- if (!arguments.length) {
- return v.conf.labelDistance;
- }
- v.conf.labelDistance = value;
- if (v.status.graphStarted) {
- v.tools.createCustomizeWizardIfNotRendering();
- }
- return graph;
- };
-
+ /**
+ * The distance of the self link path around a node. Needs a `render` call to take into effect:
+ *
+ * example.selfLinkDistance(25).render();
+ * @see {@link module:API.linkDistance}
+ * @param {number} [value=20] - The new self link distance value.
+ * @returns {(number|Object)} The current self link distance value if no parameter is given or the graph object for method chaining.
+ */
graph.selfLinkDistance = function(value) {
if (!arguments.length) {
return v.conf.selfLinkDistance;
@@ -3854,6 +4530,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * The distance between nodes centers. Needs a `render` call to take into effect:
+ *
+ * example.linkDistance(60).render();
+ * @see {@link module:API.selfLinkDistance}
+ * @param {number} [value=80] - The new link distance value.
+ * @returns {(number|Object)} The current link distance value if no parameter is given or the graph object for method chaining.
+ */
graph.linkDistance = function(value) {
if (!arguments.length) {
return v.conf.linkDistance;
@@ -3866,6 +4550,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the charge strength to the specified value. For more informations have a look at the [D3 API Reference](https://github.com/d3/d3-3.x-api-reference/blob/master/Force-Layout.md#charge). Needs a `render` call to take into effect:
+ *
+ * example.charge(-200).render();
+ * @see {@link module:API.chargeDistance}
+ * @param {number} [value=-350] - The new charge value.
+ * @returns {(number|Object)} The current charge value if no parameter is given or the graph object for method chaining.
+ */
graph.charge = function(value) {
if (!arguments.length) {
return v.conf.charge;
@@ -3878,6 +4570,14 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the maximum distance over which charge forces are applied. For more informations have a look at the [D3 API Reference](https://github.com/d3/d3-3.x-api-reference/blob/master/Force-Layout.md#chargeDistance). This option is not shown in the customize wizard. Needs a `render` call to take into effect:
+ *
+ * example.chargeDistance(200).render();
+ * @see {@link module:API.charge}
+ * @param {number} [value=Infinity] - The new charge distance value.
+ * @returns {(number|Object)} The current charge distance value if no parameter is given or the graph object for method chaining.
+ */
graph.chargeDistance = function(value) {
if (!arguments.length) {
return v.conf.chargeDistance;
@@ -3890,6 +4590,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the gravitational strength to the specified numerical value. For more informations see the [D3 API Reference](https://github.com/d3/d3-3.x-api-reference/blob/master/Force-Layout.md#gravity). Needs a `render` call to take into effect:
+ *
+ * example.gravity(0.3).render();
+ * @param {number} [value=0.1] - The new gravity value.
+ * @returns {(number|Object)} The current gravity value if no parameter is given or the graph object for method chaining.
+ */
graph.gravity = function(value) {
if (!arguments.length) {
return v.conf.gravity;
@@ -3902,6 +4609,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the strength (rigidity) of links to the specified value in the range [0,1]. For more informations see the [D3 API Reference](https://github.com/d3/d3-3.x-api-reference/blob/master/Force-Layout.md#linkStrength). Needs a `render` call to take into effect:
+ *
+ * example.linkStrength(0.1).render();
+ * @param {number} [value=1] - The new link strength value.
+ * @returns {(number|Object)} The current link strength value if no parameter is given or the graph object for method chaining.
+ */
graph.linkStrength = function(value) {
if (!arguments.length) {
return v.conf.linkStrength;
@@ -3914,6 +4628,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the friction coefficient to the specified value. For more informations have a look at the [D3 API Reference](https://github.com/d3/d3-3.x-api-reference/blob/master/Force-Layout.md#friction). Needs a `render` call to take into effect:
+ *
+ * example.friction(0.4).render();
+ * @param {number} [value=0.9] - The new friction value.
+ * @returns {(number|Object)} The current friction value if no parameter is given or the graph object for method chaining.
+ */
graph.friction = function(value) {
if (!arguments.length) {
return v.conf.friction;
@@ -3926,6 +4647,13 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the Barnes–Hut approximation criterion to the specified value. For more informations see the [D3 API Reference](https://github.com/d3/d3-3.x-api-reference/blob/master/Force-Layout.md#theta). On smaller graphs with not so many nodes you will likely see no difference when you change this value. Needs a `render` call to take into effect:
+ *
+ * example.theta(0.1).render();
+ * @param {number} [value=0.8] - The new theta value.
+ * @returns {(number|Object)} The current theta value if no parameter is given or the graph object for method chaining.
+ */
graph.theta = function(value) {
if (!arguments.length) {
return v.conf.theta;
@@ -3938,7 +4666,21 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
- graph.positions = function(value) {
+ /**
+ * Gets or sets the current positions of all nodes. This lets you save and load a specific layout or modify the current positions (of fixed nodes - if you have no fixed nodes then the nodes will likely fall back to their previous positions because of the working forces). Works nice together with the `pinMode`. Needs a `resume` call to take into effect:
+ *
+ * // get current positions: Array of objects like [{"ID":"7839","x":200,"y":100,"fixed":1},...])
+ * var pos = example.positions();
+ * // set positions
+ * example.positions(pos.map(function(p){ p.x += 10; return p; })).resume();
+ *
+ * // all in one ;-)
+ * example.positions( example.positions().map(function(p){ p.x += 10; return p; }) ).resume();
+ * @see {@link module:API.pinMode}
+ * @param {Object} [positionsArray] - The new positions array.
+ * @returns {Object} The current positions array if no parameter is given or the graph object for method chaining.
+ */
+ graph.positions = function(positionsArray) {
if (!arguments.length) {
var positions = [];
v.data.nodes.forEach(function(n) {
@@ -3952,8 +4694,8 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return positions;
} else {
if (v.status.graphReady) {
- if (value.constructor === Array) {
- value.forEach(function(n) {
+ if (positionsArray.constructor === Array) {
+ positionsArray.forEach(function(n) {
if (v.data.idLookup[n.ID] !== undefined) {
v.data.idLookup[n.ID].fixed = v.tools.parseBool(n.fixed);
v.data.idLookup[n.ID].x = v.data.idLookup[n.ID].px = n.x;
@@ -3965,28 +4707,96 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
"node positions");
}
} else {
- v.conf.positions = value; // we do positioning later after start() is called
+ v.conf.positions = positionsArray; // we do positioning later after start() is called
}
return graph;
}
};
- graph.onLinkClickFunction = function(value) {
+ /**
+ * Gets or sets the function for the link click event.
+ *
+ * In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword:
+ *
+ * example.onLinkClickFunction(
+ * function(event, data){
+ * console.log("Link click - event:", event);
+ * console.log("Link click - data:", data);
+ * console.log("Link click - this:", this);
+ * }
+ * );
+ *
+ * If used as APEX plugin you can also create an APEX dynamic action on the component event “Link Click [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:
+ *
+ * console.log("Link click - event:", this.browserEvent);
+ * console.log("Link click - data:", this.data);
+ *
+ * Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.
+ *
+ * Attention: It is not so easy to click a link, because the links are so narrow - if this option is needed I recommend to switch on the zoom mode - with zoom and pan it feels more natural to click links.
+ * @param {Object} [eventFunction] - The new function.
+ * @returns {Object} The current function if no parameter is given or the graph object for method chaining.
+ */
+ graph.onLinkClickFunction = function(eventFunction) {
if (!arguments.length) {
return v.conf.onLinkClickFunction;
}
- v.conf.onLinkClickFunction = value;
+ v.conf.onLinkClickFunction = eventFunction;
return graph;
};
- graph.onNodeMouseenterFunction = function(value) {
+ /**
+ * Gets or sets the function for the node mouseenter event.
+ *
+ * In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword:
+ *
+ * example.onNodeMouseenterFunction(
+ * function(event, data){
+ * console.log("Node mouse enter - event:", event);
+ * console.log("Node mouse enter - data:", data);
+ * console.log("Node mouse enter - this:", this);
+ * }
+ * );
+ *
+ * If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Mouse Enter [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:
+ *
+ * console.log("Node mouse enter - event:", this.browserEvent);
+ * console.log("Node mouse enter - data:", this.data);
+ *
+ * Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.
+ * @param {Object} [eventFunction] - The new function.
+ * @returns {Object} The current function if no parameter is given or the graph object for method chaining.
+ */
+ graph.onNodeMouseenterFunction = function(eventFunction) {
if (!arguments.length) {
return v.conf.onNodeMouseenterFunction;
}
- v.conf.onNodeMouseenterFunction = value;
+ v.conf.onNodeMouseenterFunction = eventFunction;
return graph;
};
+ /**
+ * Gets or sets the function for the node mouseleave event.
+ *
+ * In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword:
+ *
+ * example.onNodeMouseleaveFunction(
+ * function(event, data){
+ * console.log("Node mouse leave - event:", event);
+ * console.log("Node mouse leave - data:", data);
+ * console.log("Node mouse leave - this:", this);
+ * }
+ * );
+ *
+ * If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Mouse Leave [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:
+ *
+ * console.log("Node mouse leave - event:", this.browserEvent);
+ * console.log("Node mouse leave - data:", this.data);
+ *
+ * Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.
+ * @param {Object} [eventFunction] - The new function.
+ * @returns {Object} The current function if no parameter is given or the graph object for method chaining.
+ */
graph.onNodeMouseleaveFunction = function(value) {
if (!arguments.length) {
return v.conf.onNodeMouseleaveFunction;
@@ -3995,6 +4805,28 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the function for the node click event.
+ *
+ * In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword:
+ *
+ * example.onNodeClickFunction(
+ * function(event, data){
+ * console.log("Node click - event:", event);
+ * console.log("Node click - data:", data);
+ * console.log("Node click - this:", this);
+ * }
+ * );
+ *
+ * If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Click [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:
+ *
+ * console.log("Node click - event:", this.browserEvent);
+ * console.log("Node click - data:", this.data);
+ *
+ * Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.
+ * @param {Object} [eventFunction] - The new function.
+ * @returns {Object} The current function if no parameter is given or the graph object for method chaining.
+ */
graph.onNodeClickFunction = function(value) {
if (!arguments.length) {
return v.conf.onNodeClickFunction;
@@ -4003,6 +4835,28 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the function for the node dblclick event.
+ *
+ * In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword:
+ *
+ * example.onNodeDblclickFunction(
+ * function(event, data){
+ * console.log("Node double click - event:", event);
+ * console.log("Node double click - data:", data);
+ * console.log("Node double click - this:", this);
+ * }
+ * );
+ *
+ * If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Double Click [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:
+ *
+ * console.log("Node double click - event:", this.browserEvent);
+ * console.log("Node double click - data:", this.data);
+ *
+ * Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.
+ * @param {Object} [eventFunction] - The new function.
+ * @returns {Object} The current function if no parameter is given or the graph object for method chaining.
+ */
graph.onNodeDblclickFunction = function(value) {
if (!arguments.length) {
return v.conf.onNodeDblclickFunction;
@@ -4011,6 +4865,28 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the function for the node contextmenu event.
+ *
+ * In the first two parameters you get the event and the d3 node data, inside your function you have access to the DOM node with the this keyword:
+ *
+ * example.onNodeContextmenuFunction(
+ * function(event, data){
+ * console.log("Node contextmenu - event:", event);
+ * console.log("Node contextmenu - data:", data);
+ * console.log("Node contextmenu - this:", this);
+ * }
+ * );
+ *
+ * If used as APEX plugin you can also create an APEX dynamic action on the component event “Node Contextmenu [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:
+ *
+ * console.log("Node contextmenu - event:", this.browserEvent);
+ * console.log("Node contextmenu - data:", this.data);
+ *
+ * Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.
+ * @param {Object} [eventFunction] - The new function.
+ * @returns {Object} The current function if no parameter is given or the graph object for method chaining.
+ */
graph.onNodeContextmenuFunction = function(value) {
if (!arguments.length) {
return v.conf.onNodeContextmenuFunction;
@@ -4019,6 +4895,28 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the function for the lassostart event.
+ *
+ * In the first two parameters you get the event and the d3 lasso data, inside your function you have access to the DOM node with the this keyword. In case of the lasso this is refering the svg container element, because the lasso itself is not interesting:
+ *
+ * example.onLassoStartFunction(
+ * function(event, data){
+ * console.log("Lasso start - event:", event);
+ * console.log("Lasso start - data:", data);
+ * console.log("Lasso start - this:", this);
+ * }
+ * );
+ *
+ * If used as APEX plugin you can also create an APEX dynamic action on the component event “Lasso Start [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:
+ *
+ * console.log("Lasso start - event:", this.browserEvent);
+ * console.log("Lasso start - data:", this.data);
+ *
+ * Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.
+ * @param {Object} [eventFunction] - The new function.
+ * @returns {Object} The current function if no parameter is given or the graph object for method chaining.
+ */
graph.onLassoStartFunction = function(value) {
if (!arguments.length) {
return v.conf.onLassoStartFunction;
@@ -4027,6 +4925,28 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
+ /**
+ * Gets or sets the function for the lassoend event.
+ *
+ * In the first two parameters you get the event and the d3 lasso data, inside your function you have access to the DOM node with the this keyword. In case of the lasso this is refering the svg container element, because the lasso itself is not interesting:
+ *
+ * example.onLassoEndFunction(
+ * function(event, data){
+ * console.log("Lasso end - event:", event);
+ * console.log("Lasso end - data:", data);
+ * console.log("Lasso end - this:", this);
+ * }
+ * );
+ *
+ * If used as APEX plugin you can also create an APEX dynamic action on the component event “Lasso End [D3 - Force Layout]” on your graph region. If you do so, you can access the event and data by executing JavaScript code in this way:
+ *
+ * console.log("Lasso end - event:", this.browserEvent);
+ * console.log("Lasso end - data:", this.data);
+ *
+ * Please refer also to the APEX dynamic action documentation and keep in mind, that the data is the same in both ways but the event differs, because APEX provide a jQuery event and the Plugin the D3 original event.
+ * @param {Object} [eventFunction] - The new function.
+ * @returns {Object} The current function if no parameter is given or the graph object for method chaining.
+ */
graph.onLassoEndFunction = function(value) {
if (!arguments.length) {
return v.conf.onLassoEndFunction;
@@ -4035,23 +4955,71 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
- graph.sampleData = function(value) {
+ /**
+ * Gets or sets the sample data. This makes only sense before the first start, because only on the first start without data available the sample data is used. After the first start you can provide new data with the start method. Example:
+ *
+ * //first start
+ * example.sampleData('...').start();
+ *
+ * //later
+ * example.start('...');
+ * @see {@link module:API.start}
+ * @param {(string|Object)} [data] - The new sample data as XML string, JSON string or JSON object.
+ * @returns {Object} The current sample data in JSON format if no parameter is given or the graph object for method chaining.
+ */
+ graph.sampleData = function(data) {
if (!arguments.length) {
return v.data.sampleData;
}
- v.data.sampleData = value;
+ v.data.sampleData = data;
return graph;
};
+
+ /**
+ * Returns the current graph data as JSON object. This method expects no parameter and terminates the method chain. Example:
+ *
+ * //JSON object
+ * example.data();
+ *
+ * //stringified JSON object
+ * JSON.stringify(example.data());
+ * @see {@link module:API.nodeDataById}
+ * @see {@link module:API.start}
+ * @returns {Object} The current graph data.
+ */
graph.data = function() {
return v.data.dataConverted;
};
- graph.nodeDataById = function(ID) {
- return v.data.idLookup[ID];
+ /**
+ * Returns the data from a specific node as JSON object. This method expects a node ID as parameter and terminates the method chain. Example:
+ *
+ * //get the data from the node with the ID 8888
+ * example.nodeDataById('8888');
+ *
+ * //get the data from the node with the ID 'myAlphanumericID'
+ * example.nodeDataById('myAlphanumericID');
+ * @see {@link module:API.data}
+ * @param {string} id - The node id.
+ * @returns {Object} The node data.
+ */
+ graph.nodeDataById = function(id) {
+ return v.data.idLookup[id];
};
- graph.options = function(value) {
+ /**
+ * Get or set the whole configuration with one call. Ouput includes all options, which are accessible via the API methods including the registered event functions:
+ *
+ * //get the current configuration
+ * example.options();
+ * //set the new configuration
+ * example.options( { pinMode: true, ... } );
+ * @see {@link module:API.optionsCustomizationWizard}
+ * @param {Object} [options] - Your new options.
+ * @returns {Object} Your current options or the graph object for method chaining.
+ */
+ graph.options = function(options) {
var key;
if (!arguments.length) {
var conf = {};
@@ -4076,12 +5044,23 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
}
return conf;
} else {
- v.tools.applyConfigurationObject(value);
+ v.tools.applyConfigurationObject(options);
return graph;
}
};
- graph.optionsCustomizationWizard = function(value) {
+ /**
+ * Get or set the whole configuration with one call. Output includes only the options, which are accessible via the customization wizard:
+ *
+ * //get the current configuration
+ * example.optionsCustomizationWizard();
+ * //set the new configuration
+ * example.optionsCustomizationWizard( { pinMode: true, ... } );
+ * @see {@link module:API.options}
+ * @param {Object} [options] - Your new options.
+ * @returns {Object} Your current options or the graph object for method chaining.
+ */
+ graph.optionsCustomizationWizard = function(options) {
var key;
if (!arguments.length) {
var conf = {};
@@ -4100,11 +5079,19 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
}
return conf;
} else {
- v.tools.applyConfigurationObject(value);
+ v.tools.applyConfigurationObject(options);
return graph;
}
};
+ /**
+ * Gets or sets the customize mode. If true, the customizing wizard is opened, otherwise closed.
+ *
+ * example.customize(true);
+ * @see {@link module:API.debug}
+ * @param {boolean} [value] - The new mode.
+ * @returns {(boolean|Object)} The current mode if no parameter is given or the graph object for method chaining.
+ */
graph.customize = function(value) {
if (!arguments.length) {
return v.status.customize;
@@ -4113,13 +5100,25 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
if (v.status.graphStarted) {
if (v.status.customize) {
v.tools.createCustomizeWizard();
+ v.tools.removeCustomizeLink();
} else {
v.tools.removeCustomizeWizard();
+ if (v.conf.debug) {
+ v.tools.createCustomizeLink();
+ }
}
}
return graph;
};
+ /**
+ * Gets or sets the debug mode. When debug is enabled, there is a link rendered in the SVG to start the customize wizard and debug messages are written to the console.
+ *
+ * example.debug(true);
+ * @see {@link module:API.customize}
+ * @param {boolean} [value] - The new mode.
+ * @returns {(boolean|Object)} The current mode if no parameter is given or the graph object for method chaining.
+ */
graph.debug = function(value) {
if (!arguments.length) {
return v.conf.debug;
@@ -4135,40 +5134,62 @@ function netGobrechtsD3Force(pDomContainerId, pOptions, pApexPluginId, pApexPage
return graph;
};
- graph.releaseFixedNodes = function() {
- if (v.status.graphStarted) {
- v.main.nodes.each(function(n) {
- n.fixed = 0;
- });
- }
- return graph;
- };
-
+ /**
+ * Returns the detected user agent. Expects no parameter and terminates the method chain:
+ *
+ * example.userAgent();
+ * @see {@link module:API.inspect}
+ * @returns {string} The detected user agent.
+ */
graph.userAgent = function() {
return v.status.userAgent;
};
- // public inspect function: to inspect the global object, which holds all data, functions and references
+ /**
+ * Shows the current closure object, which holds all functions and data. This method expects no parameter and terminates the method chain:
+ *
+ * example.inspect();
+ * @see {@link module:API.userAgent}
+ * @returns {Object} The graph's internal object with all functions and data.
+ */
graph.inspect = function() {
return v;
};
+ /**
+ * Shows the current plugin version. This method expects no parameter and terminates the method chain:
+ *
+ * example.version();
+ * @see {@link module:API.userAgent}
+ * @returns {string} The plugin version.
+ */
graph.version = function() {
return v.version;
};
/*******************************************************************************************************************
* Startup code - runs one time after the initialization of a new chart - example:
- * var myChart = net_gobrechts_d3_force( pDomContainerId, pConf, pApexPluginId ).start();
+ * var myChart = net_gobrechts_d3_force( domContainerId, pConf, apexPluginId ).start();
*/
- // bind to the apexrefresh event, so that this region can be refreshed by a dynamic action
if (v.status.apexPluginId) {
+ // bind to the apexrefresh event, so that this region can be refreshed by a dynamic action
apex.jQuery("#" + v.dom.containerId).bind("apexrefresh", function() {
graph.start();
});
+ //rerender on window resize
+ apex.jQuery(window).on("apexwindowresized", function() {
+ graph.render();
+ });
+ apex.jQuery("#t_Button_navControl").click(function() {
+ setTimeout(function() {
+ graph.render();
+ }, 500);
+ });
+
}
+
// final return
return graph;
diff --git a/share/public/javascripts/d3.min.js b/share/public/javascripts/d3.min.js
index 1984d172..2856dd2f 100644
--- a/share/public/javascripts/d3.min.js
+++ b/share/public/javascripts/d3.min.js
@@ -1,5 +1,5 @@
-!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:0/0}function r(n){return null===n?0/0:+n}function u(n){return!isNaN(n)}function i(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)<0?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)>0?u=i:r=i+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function c(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function l(){this._=Object.create(null)}function s(n){return(n+="")===pa||n[0]===va?va+n:n}function f(n){return(n+="")[0]===va?n.slice(1):n}function h(n){return s(n)in this._}function g(n){return(n=s(n))in this._&&delete this._[n]}function p(){var n=[];for(var t in this._)n.push(f(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function m(){this._=Object.create(null)}function y(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=da.length;r>e;++e){var u=da[e]+t;if(u in n)return u}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,u=-1,i=r.length;++ue;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function Z(n){return ya(n,Sa),n}function V(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var l=ka.get(n);return l&&(n=l,c=B),a?t?u:r:t?b:i}function $(n,t){return function(e){var r=ta.event;ta.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ta.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Aa,u="click"+r,i=ta.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ea&&(Ea="onselectstart"in e?!1:x(e.style,"userSelect")),Ea){var o=n(e).style,a=o[Ea];o[Ea]="none"}return function(n){if(i.on(r,null),Ea&&(o[Ea]=a),n){var t=function(){i.on(u,null)};i.on(u,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var u=r.createSVGPoint();if(0>Na){var i=t(n);if(i.scrollX||i.scrollY){r=ta.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Na=!(o.f||o.e),r.remove()}}return Na?(u.x=e.pageX,u.y=e.pageY):(u.x=e.clientX,u.y=e.clientY),u=u.matrixTransform(n.getScreenCTM().inverse()),[u.x,u.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ta.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nt(n){return n>1?0:-1>n?qa:Math.acos(n)}function tt(n){return n>1?Ra:-1>n?-Ra:Math.asin(n)}function et(n){return((n=Math.exp(n))-1/n)/2}function rt(n){return((n=Math.exp(n))+1/n)/2}function ut(n){return((n=Math.exp(2*n))-1)/(n+1)}function it(n){return(n=Math.sin(n/2))*n}function ot(){}function at(n,t,e){return this instanceof at?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof at?new at(n.h,n.s,n.l):bt(""+n,_t,at):new at(n,t,e)}function ct(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,new mt(u(n+120),u(n),u(n-120))}function lt(n,t,e){return this instanceof lt?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof lt?new lt(n.h,n.c,n.l):n instanceof ft?gt(n.l,n.a,n.b):gt((n=wt((n=ta.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new lt(n,t,e)}function st(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new ft(e,Math.cos(n*=Da)*t,Math.sin(n)*t)}function ft(n,t,e){return this instanceof ft?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof ft?new ft(n.l,n.a,n.b):n instanceof lt?st(n.h,n.c,n.l):wt((n=mt(n)).r,n.g,n.b):new ft(n,t,e)}function ht(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=pt(u)*Xa,r=pt(r)*$a,i=pt(i)*Ba,new mt(dt(3.2404542*u-1.5371385*r-.4985314*i),dt(-.969266*u+1.8760108*r+.041556*i),dt(.0556434*u-.2040259*r+1.0572252*i))}function gt(n,t,e){return n>0?new lt(Math.atan2(e,t)*Pa,Math.sqrt(t*t+e*e),n):new lt(0/0,0/0,n)}function pt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function vt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function dt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mt(n,t,e){return this instanceof mt?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mt?new mt(n.r,n.g,n.b):bt(""+n,mt,ct):new mt(n,t,e)}function yt(n){return new mt(n>>16,n>>8&255,255&n)}function Mt(n){return yt(n)+""}function xt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function bt(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=Ga.get(n))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function _t(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),new at(r,u,c)}function wt(n,t,e){n=St(n),t=St(t),e=St(e);var r=vt((.4124564*n+.3575761*t+.1804375*e)/Xa),u=vt((.2126729*n+.7151522*t+.072175*e)/$a),i=vt((.0193339*n+.119192*t+.9503041*e)/Ba);return ft(116*u-16,500*(r-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function At(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Nt(t,e,n,r)}}function Nt(n,t,e,r){function u(){var n,t=c.status;if(!t&&zt(c)||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return void o.error.call(i,r)}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=ta.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=ta.event;ta.event=n;try{o.progress.call(i,c)}finally{ta.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(l=n,i):l},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(ra(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var s in a)c.setRequestHeader(s,a[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},ta.rebind(i,o,"on"),null==r?i:i.get(Ct(r))}function Ct(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function zt(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qt(){var n=Lt(),t=Tt()-n;t>24?(isFinite(t)&&(clearTimeout(tc),tc=setTimeout(qt,t)),nc=0):(nc=1,rc(qt))}function Lt(){var n=Date.now();for(ec=Ka;ec;)n>=ec.t&&(ec.f=ec.c(n-ec.t)),ec=ec.n;return n}function Tt(){for(var n,t=Ka,e=1/0;t;)t.f?t=n?n.n=t.n:Ka=t.n:(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Pt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r&&e?function(n,t){for(var u=n.length,i=[],o=0,a=r[0],c=0;u>0&&a>0&&(c+a+1>t&&(a=Math.max(1,t-c)),i.push(n.substring(u-=a,u+a)),!((c+=a+1)>t));)a=r[o=(o+1)%r.length];return i.reverse().join(e)}:y;return function(n){var e=ic.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",c=e[4]||"",l=e[5],s=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1,y=!0;switch(h&&(h=+h.substring(1)),(l||"0"===r&&"="===o)&&(l=r="0",o="="),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":y=!1;case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=oc.get(g)||Ut;var M=l&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>p){var c=ta.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=y?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!l&&f&&(x=i(x,1/0));var S=v.length+x.length+b.length+(M?0:u.length),k=s>S?new Array(S=s-S+1).join(r):"";return M&&(x=i(k+x,k.length?s-b.length:1/0)),u+=v,n=x+b,("<"===o?u+n+k:">"===o?k+u+n:"^"===o?k.substring(0,S>>=1)+u+n+k.substring(S):u+(M?n:k+n))+e}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ft(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new cc(e-1)),1),e}function i(n,e){return t(n=new cc(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{cc=jt;var r=new jt;return r._=n,o(r,t,e)}finally{cc=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ht(n);return c.floor=c,c.round=Ht(r),c.ceil=Ht(u),c.offset=Ht(i),c.range=a,n}function Ht(n){return function(t,e){try{cc=jt;var r=new jt;return r._=t,n(r,e)._}finally{cc=Date}}}function Ot(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++aa;){if(r>=l)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in sc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.slice(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,N.c.toString(),t,r)}function c(n,t,r){return e(n,N.x.toString(),t,r)}function l(n,t,r){return e(n,N.X.toString(),t,r)}function s(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{cc=jt;var t=new cc;return t._=n,r(t)}finally{cc=Date}}var r=t(n);return e.parse=function(n){try{cc=jt;var t=r.parse(n);return t&&t._}finally{cc=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ae;var M=ta.map(),x=Yt(v),b=Zt(v),_=Yt(d),w=Zt(d),S=Yt(m),k=Zt(m),E=Yt(y),A=Zt(y);p.forEach(function(n,t){M.set(n.toLowerCase(),t)});var N={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return It(n.getDate(),t,2)},e:function(n,t){return It(n.getDate(),t,2)},H:function(n,t){return It(n.getHours(),t,2)},I:function(n,t){return It(n.getHours()%12||12,t,2)},j:function(n,t){return It(1+ac.dayOfYear(n),t,3)},L:function(n,t){return It(n.getMilliseconds(),t,3)},m:function(n,t){return It(n.getMonth()+1,t,2)},M:function(n,t){return It(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return It(n.getSeconds(),t,2)},U:function(n,t){return It(ac.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return It(ac.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return It(n.getFullYear()%100,t,2)},Y:function(n,t){return It(n.getFullYear()%1e4,t,4)},Z:ie,"%":function(){return"%"}},C={a:r,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:te,I:te,j:ne,L:ue,m:Kt,M:ee,p:s,S:re,U:Xt,w:Vt,W:$t,x:c,X:l,y:Wt,Y:Bt,Z:Jt,"%":oe};return t}function It(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function Yt(n){return new RegExp("^(?:"+n.map(ta.requote).join("|")+")","i")}function Zt(n){for(var t=new l,e=-1,r=n.length;++e68?1900:2e3)}function Kt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Qt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function ne(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function te(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ee(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function re(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ue(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ie(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=ga(t)/60|0,u=ga(t)%60;return e+It(r,"0",2)+It(u,"0",2)}function oe(n,t,e){hc.lastIndex=0;var r=hc.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ae(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,c=Math.cos(t),l=Math.sin(t),s=i*l,f=u*c+s*Math.cos(a),h=s*o*Math.sin(a);yc.add(Math.atan2(h,f)),r=n,u=c,i=l}var t,e,r,u,i;Mc.point=function(o,a){Mc.point=n,r=(t=o)*Da,u=Math.cos(a=(e=a)*Da/2+qa/4),i=Math.sin(a)},Mc.lineEnd=function(){n(t,e)}}function pe(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function ve(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function de(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function me(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ye(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function Me(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function xe(n){return[Math.atan2(n[1],n[0]),tt(n[2])]}function be(n,t){return ga(n[0]-t[0])a;++a)u.point((e=n[a])[0],e[1]);return void u.lineEnd()}var c=new qe(e,n,null,!0),l=new qe(e,null,c,!1);c.o=l,i.push(c),o.push(l),c=new qe(r,n,null,!1),l=new qe(r,null,c,!0),c.o=l,i.push(c),o.push(l)}}),o.sort(t),ze(i),ze(o),i.length){for(var a=0,c=e,l=o.length;l>a;++a)o[a].e=c=!c;for(var s,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;s=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,l=s.length;l>a;++a)u.point((f=s[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){s=g.p.z;for(var a=s.length-1;a>=0;--a)u.point((f=s[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,s=g.z,p=!p}while(!g.v);u.lineEnd()}}}function ze(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r0){for(b||(i.polygonStart(),b=!0),i.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Te))}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:l,polygonStart:function(){y.point=s,y.lineStart=f,y.lineEnd=h,g=[],p=[]},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=l,g=ta.merge(g);var n=Fe(m,p);g.length?(b||(i.polygonStart(),b=!0),Ce(g,De,n,e,i)):n&&(b||(i.polygonStart(),b=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),b&&(i.polygonEnd(),b=!1),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},M=Re(),x=t(M),b=!1;return y}}function Te(n){return n.length>1}function Re(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function De(n,t){return((n=n.x)[0]<0?n[1]-Ra-Ca:Ra-n[1])-((t=t.x)[0]<0?t[1]-Ra-Ca:Ra-t[1])}function Pe(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?qa:-qa,c=ga(i-e);ga(c-qa)0?Ra:-Ra),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=qa&&(ga(e-u)Ca?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function je(n,t,e,r){var u;if(null==n)u=e*Ra,r.point(-qa,u),r.point(0,u),r.point(qa,u),r.point(qa,0),r.point(qa,-u),r.point(0,-u),r.point(-qa,-u),r.point(-qa,0),r.point(-qa,u);else if(ga(n[0]-t[0])>Ca){var i=n[0]a;++a){var l=t[a],s=l.length;if(s)for(var f=l[0],h=f[0],g=f[1]/2+qa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===s&&(d=0),n=l[d];var m=n[0],y=n[1]/2+qa/4,M=Math.sin(y),x=Math.cos(y),b=m-h,_=b>=0?1:-1,w=_*b,S=w>qa,k=p*M;if(yc.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),i+=S?b+_*La:b,S^h>=e^m>=e){var E=de(pe(f),pe(n));Me(E);var A=de(u,E);Me(A);var N=(S^b>=0?-1:1)*tt(A[2]);(r>N||r===N&&(E[0]||E[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=m,p=M,v=x,f=n}}return(-Ca>i||Ca>i&&0>yc)^1&o}function He(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,l,s;return{lineStart:function(){l=c=!1,s=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?qa:-qa),h):0;if(!e&&(l=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(be(e,g)||be(p,g))&&(p[0]+=Ca,p[1]+=Ca,v=t(p[0],p[1]))),v!==c)s=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(s=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&be(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return s|(l&&c)<<1}}}function r(n,t,e){var r=pe(n),u=pe(t),o=[1,0,0],a=de(r,u),c=ve(a,a),l=a[0],s=c-l*l;if(!s)return!e&&n;var f=i*c/s,h=-i*l/s,g=de(o,a),p=ye(o,f),v=ye(a,h);me(p,v);var d=g,m=ve(p,d),y=ve(d,d),M=m*m-y*(ve(p,p)-1);if(!(0>M)){var x=Math.sqrt(M),b=ye(d,(-m-x)/y);if(me(b,p),b=xe(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(_=w,w=S,S=_);var A=S-w,N=ga(A-qa)A;if(!N&&k>E&&(_=k,k=E,E=_),C?N?k+E>0^b[1]<(ga(b[0]-w)qa^(w<=b[0]&&b[0]<=S)){var z=ye(d,(-m+x)/y);return me(z,p),[b,xe(z)]}}}function u(t,e){var r=o?n:qa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=ga(i)>Ca,c=gr(n,6*Da);return Le(t,e,c,o?[0,-n]:[-qa,n-qa])}function Oe(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,l=o.y,s=a.x,f=a.y,h=0,g=1,p=s-c,v=f-l;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-l,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-l,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:l+h*v}),1>g&&(u.b={x:c+g*p,y:l+g*v}),u}}}}}}function Ie(n,t,e,r){function u(r,u){return ga(r[0]-n)0?0:3:ga(r[0]-e)0?2:1:ga(r[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&Q(l,i,n)>0&&++t:i[1]<=r&&Q(l,i,n)<0&&--t,l=i;return 0!==t}function l(i,a,c,l){var s=0,f=0;if(null==i||(s=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do l.point(0===s||3===s?n:e,s>1?r:t);while((s=(s+c+4)%4)!==f)}else l.point(a[0],a[1])}function s(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){s(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,b=_=0/0}function g(){v&&(p(y,M),x&&w&&A.rejoin(),v.push(A.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Tc,Math.min(Tc,n)),t=Math.max(-Tc,Math.min(Tc,t));var e=s(n,t);if(d&&m.push([n,t]),S)y=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};N(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,m,y,M,x,b,_,w,S,k,E=a,A=Re(),N=Oe(n,t,e,r),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=ta.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Ce(v,i,t,l,a),a.polygonEnd()),v=d=m=null}};return C}}function Ye(n){var t=0,e=qa/3,r=ir(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*qa/180,e=n[1]*qa/180):[t/qa*180,e/qa*180]},u}function Ze(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,tt((i-(n*n+e*e)*u*u)/(2*u))]},e}function Ve(){function n(n,t){Dc+=u*n-r*t,r=n,u=t}var t,e,r,u;Hc.point=function(i,o){Hc.point=n,t=r=i,e=u=o},Hc.lineEnd=function(){n(t,e)}}function Xe(n,t){Pc>n&&(Pc=n),n>jc&&(jc=n),Uc>t&&(Uc=t),t>Fc&&(Fc=t)}function $e(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Be(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Be(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Be(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function We(n,t){_c+=n,wc+=t,++Sc}function Je(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);kc+=o*(t+n)/2,Ec+=o*(e+r)/2,Ac+=o,We(t=n,e=r)}var t,e;Ic.point=function(r,u){Ic.point=n,We(t=r,e=u)}}function Ge(){Ic.point=We}function Ke(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);kc+=o*(r+n)/2,Ec+=o*(u+t)/2,Ac+=o,o=u*n-r*t,Nc+=o*(r+n),Cc+=o*(u+t),zc+=3*o,We(r=n,u=t)}var t,e,r,u;Ic.point=function(i,o){Ic.point=n,We(t=r=i,e=u=o)},Ic.lineEnd=function(){n(t,e)}}function Qe(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,La)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function nr(n){function t(n){return(a?r:e)(n)}function e(t){return rr(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=0/0,S.point=i,t.lineStart()}function i(e,r){var i=pe([e,r]),o=n(e,r);u(M,x,y,b,_,w,M=o[0],x=o[1],y=e,b=i[0],_=i[1],w=i[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=l,S.lineEnd=s}function l(n,t){i(f=n,h=t),g=M,p=x,v=b,d=_,m=w,S.point=i}function s(){u(M,x,y,b,_,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c
-},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,l,s,f,h,g,p,v,d,m){var y=s-t,M=f-e,x=y*y+M*M;if(x>4*i&&d--){var b=a+g,_=c+p,w=l+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),E=ga(ga(w)-1)i||ga((y*z+M*q)/x-.5)>.3||o>a*g+c*p+l*v)&&(u(t,e,r,a,c,l,N,C,E,b/=S,_/=S,w,d,m),m.point(N,C),u(N,C,E,b,_,w,s,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Da),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function tr(n){var t=nr(function(t,e){return n([t*Pa,e*Pa])});return function(n){return or(t(n))}}function er(n){this.stream=n}function rr(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function ur(n){return ir(function(){return n})()}function ir(n){function t(n){return n=a(n[0]*Da,n[1]*Da),[n[0]*h+c,l-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Pa,n[1]*Pa]}function r(){a=Ae(o=lr(m,M,x),i);var n=i(v,d);return c=g-n[0]*h,l=p+n[1]*h,u()}function u(){return s&&(s.valid=!1,s=null),t}var i,o,a,c,l,s,f=nr(function(n,t){return n=i(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,M=0,x=0,b=Lc,_=y,w=null,S=null;return t.stream=function(n){return s&&(s.valid=!1),s=or(b(o,f(_(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Lc):He((w=+n)*Da),u()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Ie(n[0][0],n[0][1],n[1][0],n[1][1]):y,u()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Da,d=n[1]%360*Da,r()):[v*Pa,d*Pa]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Da,M=n[1]%360*Da,x=n.length>2?n[2]%360*Da:0,r()):[m*Pa,M*Pa,x*Pa]},ta.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function or(n){return rr(n,function(t,e){n.point(t*Da,e*Da)})}function ar(n,t){return[n,t]}function cr(n,t){return[n>qa?n-La:-qa>n?n+La:n,t]}function lr(n,t,e){return n?t||e?Ae(fr(n),hr(t,e)):fr(n):t||e?hr(t,e):cr}function sr(n){return function(t,e){return t+=n,[t>qa?t-La:-qa>t?t+La:t,e]}}function fr(n){var t=sr(n);return t.invert=sr(-n),t}function hr(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*r+a*u;return[Math.atan2(c*i-s*o,a*r-l*u),tt(s*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*i-c*o;return[Math.atan2(c*i+l*o,a*r+s*u),tt(s*r-a*u)]},e}function gr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=pr(e,u),i=pr(e,i),(o>0?i>u:u>i)&&(u+=o*La)):(u=n+o*La,i=n-.5*c);for(var l,s=u;o>0?s>i:i>s;s-=c)a.point((l=xe([e,-r*Math.cos(s),-r*Math.sin(s)]))[0],l[1])}}function pr(n,t){var e=pe(t);e[0]-=n,Me(e);var r=nt(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ca)%(2*Math.PI)}function vr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function dr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function mr(n){return n.source}function yr(n){return n.target}function Mr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),l=u*Math.sin(n),s=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(it(r-t)+u*o*it(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*s,u=e*l+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Pa,Math.atan2(o,Math.sqrt(r*r+u*u))*Pa]}:function(){return[n*Pa,t*Pa]};return p.distance=h,p}function xr(){function n(n,u){var i=Math.sin(u*=Da),o=Math.cos(u),a=ga((n*=Da)-t),c=Math.cos(a);Yc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Zc.point=function(u,i){t=u*Da,e=Math.sin(i*=Da),r=Math.cos(i),Zc.point=n},Zc.lineEnd=function(){Zc.point=Zc.lineEnd=b}}function br(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function _r(n,t){function e(n,t){o>0?-Ra+Ca>t&&(t=-Ra+Ca):t>Ra-Ca&&(t=Ra-Ca);var e=o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(qa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=K(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ra]},e):Sr}function wr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return ga(u)u;u++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function zr(n,t){return n[0]-t[0]||n[1]-t[1]}function qr(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Lr(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],l=e[1],s=t[1]-c,f=r[1]-l,h=(a*(c-l)-f*(u-i))/(f*o-a*s);return[u+h*o,c+h*s]}function Tr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Rr(){tu(this),this.edge=this.site=this.circle=null}function Dr(n){var t=el.pop()||new Rr;return t.site=n,t}function Pr(n){Xr(n),Qc.remove(n),el.push(n),tu(n)}function Ur(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Pr(n);for(var c=i;c.circle&&ga(e-c.circle.x)s;++s)l=a[s],c=a[s-1],Kr(l.edge,c.site,l.site,u);c=a[0],l=a[f-1],l.edge=Jr(c.site,l.site,null,u),Vr(c),Vr(l)}function jr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Qc._;a;)if(r=Fr(a,o)-i,r>Ca)a=a.L;else{if(u=i-Hr(a,o),!(u>Ca)){r>-Ca?(t=a.P,e=a):u>-Ca?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Dr(n);if(Qc.insert(t,c),t||e){if(t===e)return Xr(t),e=Dr(t.site),Qc.insert(c,e),c.edge=e.edge=Jr(t.site,c.site),Vr(t),void Vr(e);if(!e)return void(c.edge=Jr(t.site,c.site));Xr(t),Xr(e);var l=t.site,s=l.x,f=l.y,h=n.x-s,g=n.y-f,p=e.site,v=p.x-s,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,M=v*v+d*d,x={x:(d*y-g*M)/m+s,y:(h*M-v*y)/m+f};Kr(e.edge,l,p,x),c.edge=Jr(l,n,null,x),e.edge=Jr(n,p,null,x),Vr(t),Vr(e)}}function Fr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,l=c-t;if(!l)return a;var s=a-r,f=1/i-1/l,h=s/l;return f?(-h+Math.sqrt(h*h-2*f*(s*s/(-2*l)-c+l/2+u-i/2)))/f+r:(r+a)/2}function Hr(n,t){var e=n.N;if(e)return Fr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Or(n){this.site=n,this.edges=[]}function Ir(n){for(var t,e,r,u,i,o,a,c,l,s,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Kc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)s=a[o].end(),r=s.x,u=s.y,l=a[++o%c].start(),t=l.x,e=l.y,(ga(r-t)>Ca||ga(u-e)>Ca)&&(a.splice(o,0,new Qr(Gr(i.site,s,ga(r-f)Ca?{x:f,y:ga(t-f)Ca?{x:ga(e-p)Ca?{x:h,y:ga(t-h)Ca?{x:ga(e-g)=-za)){var g=c*c+l*l,p=s*s+f*f,v=(f*g-l*p)/h,d=(c*p-s*g)/h,f=d+a,m=rl.pop()||new Zr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,M=tl._;M;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=l)return}else i={x:d,y:c};e={x:d,y:l}}else{if(i){if(i.yr||r>1)if(h>p){if(i){if(i.y>=l)return}else i={x:(c-u)/r,y:c};e={x:(l-u)/r,y:l}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.xi||f>o||r>h||u>g)){if(p=n.point){var p,v=t-n.x,d=e-n.y,m=v*v+d*d;if(c>m){var y=Math.sqrt(c=m);r=t-y,u=e-y,i=t+y,o=e+y,a=p}}for(var M=n.nodes,x=.5*(s+h),b=.5*(f+g),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:l(n,s,f,x,b);break;case 1:l(n,x,f,h,b);break;case 2:l(n,s,b,x,g);break;case 3:l(n,x,b,h,g)}}}(n,r,u,i,o),a}function gu(n,t){n=ta.rgb(n),t=ta.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+xt(Math.round(e+i*n))+xt(Math.round(r+o*n))+xt(Math.round(u+a*n))}}function pu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=mu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function vu(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function du(n,t){var e,r,u,i=il.lastIndex=ol.lastIndex=0,o=-1,a=[],c=[];for(n+="",t+="";(e=il.exec(n))&&(r=ol.exec(t));)(u=r.index)>i&&(u=t.slice(i,u),a[o]?a[o]+=u:a[++o]=u),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,c.push({i:o,x:vu(e,r)})),i=ol.lastIndex;return ir;++r)a[(e=c[r]).i]=e.x(n);return a.join("")})}function mu(n,t){for(var e,r=ta.interpolators.length;--r>=0&&!(e=ta.interpolators[r](n,t)););return e}function yu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(mu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Mu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function xu(n){return function(t){return 1-n(1-t)}}function bu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function _u(n){return n*n}function wu(n){return n*n*n}function Su(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function ku(n){return function(t){return Math.pow(t,n)}}function Eu(n){return 1-Math.cos(n*Ra)}function Au(n){return Math.pow(2,10*(n-1))}function Nu(n){return 1-Math.sqrt(1-n*n)}function Cu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/La*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*La/t)}}function zu(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function qu(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Lu(n,t){n=ta.hcl(n),t=ta.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return st(e+i*n,r+o*n,u+a*n)+""}}function Tu(n,t){n=ta.hsl(n),t=ta.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(e+i*n,r+o*n,u+a*n)+""}}function Ru(n,t){n=ta.lab(n),t=ta.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ht(e+i*n,r+o*n,u+a*n)+""}}function Du(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Pu(n){var t=[n.a,n.b],e=[n.c,n.d],r=ju(t),u=Uu(t,e),i=ju(Fu(e,t,-u))||0;t[0]*e[1]180?s+=360:s-l>180&&(l+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:vu(l,s)})):s&&r.push(r.pop()+"rotate("+s+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:vu(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:vu(g[0],p[0])},{i:e-2,x:vu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i=0;)e.push(u[r])}function Qu(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(i=n.children)&&(u=i.length))for(var u,i,o=-1;++oe;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function si(n){return n.reduce(fi,0)}function fi(n,t){return n+t[1]}function hi(n,t){return gi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function gi(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function pi(n){return[ta.min(n),ta.max(n)]}function vi(n,t){return n.value-t.value}function di(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function mi(n,t){n._pack_next=t,t._pack_prev=n}function yi(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Mi(n){function t(n){s=Math.min(n.x-n.r,s),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(l=e.length)){var e,r,u,i,o,a,c,l,s=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(xi),r=e[0],r.x=-r.r,r.y=0,t(r),l>1&&(u=e[1],u.x=u.r,u.y=0,t(u),l>2))for(i=e[2],wi(r,u,i),t(i),di(r,i),r._pack_prev=i,di(i,u),u=r._pack_next,o=3;l>o;o++){wi(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(yi(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!yi(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=e[o],i.x-=m,i.y-=y,M=Math.max(M,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=M,e.forEach(bi)}}function xi(n){n._pack_next=n._pack_prev=n}function bi(n){delete n._pack_next,delete n._pack_prev}function _i(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i=0;)t=u[i],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Ci(n,t,e){return n.a.parent===t.parent?n.a:e}function zi(n){return 1+ta.max(n,function(n){return n.y})}function qi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Li(n){var t=n.children;return t&&t.length?Li(t[0]):n}function Ti(n){var t,e=n.children;return e&&(t=e.length)?Ti(e[t-1]):n}function Ri(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Di(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function Pi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ui(n){return n.rangeExtent?n.rangeExtent():Pi(n.range())}function ji(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Fi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Hi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ml}function Oi(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Oi:ji,c=r?Iu:Ou;return o=u(n,t,c,e),a=u(t,n,c,mu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Du)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Xi(n,t)},i.tickFormat=function(t,e){return $i(n,t,e)},i.nice=function(t){return Zi(n,t),u()},i.copy=function(){return Ii(n,t,e,r)},u()}function Yi(n,t){return ta.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Zi(n,t){return Fi(n,Hi(Vi(n,t)[2]))}function Vi(n,t){null==t&&(t=10);var e=Pi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Xi(n,t){return ta.range.apply(ta,Vi(n,t))}function $i(n,t,e){var r=Vi(n,t);if(e){var u=ic.exec(e);if(u.shift(),"s"===u[8]){var i=ta.formatPrefix(Math.max(ga(r[0]),ga(r[1])));return u[7]||(u[7]="."+Bi(i.scale(r[2]))),u[8]="f",e=ta.format(u.join("")),function(n){return e(i.scale(n))+i.symbol}}u[7]||(u[7]="."+Wi(u[8],r)),e=u.join("")}else e=",."+Bi(r[2])+"f";return ta.format(e)}function Bi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Wi(n,t){var e=Bi(t[2]);return n in yl?Math.abs(e-Bi(Math.max(ga(t[0]),ga(t[1]))))+ +("e"!==n):e-2*("%"===n)}function Ji(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Fi(r.map(u),e?Math:xl);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=Pi(r),o=[],a=n[0],c=n[1],l=Math.floor(u(a)),s=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(s-l)){if(e){for(;s>l;l++)for(var h=1;f>h;h++)o.push(i(l)*h);o.push(i(l))}else for(o.push(i(l));l++0;h--)o.push(i(l)*h);for(l=0;o[l]c;s--);o=o.slice(l,s)}return o},o.tickFormat=function(n,t){if(!arguments.length)return Ml;arguments.length<2?t=Ml:"function"!=typeof t&&(t=ta.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return Ji(n.copy(),t,e,r)},Yi(o,n)}function Gi(n,t,e){function r(t){return n(u(t))}var u=Ki(t),i=Ki(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Xi(e,n)},r.tickFormat=function(n,t){return $i(e,n,t)},r.nice=function(n){return r.domain(Zi(e,n))},r.exponent=function(o){return arguments.length?(u=Ki(t=o),i=Ki(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Gi(n.copy(),t,e)},Yi(r,n)}function Ki(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Qi(n,t){function e(e){return i[((u.get(e)||("range"===t.t?u.set(e,n.push(e)):0/0))-1)%i.length]}function r(t,e){return ta.range(n.length).map(function(n){return t+e*n})}var u,i,o;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new l;for(var i,o=-1,a=r.length;++oe?[0/0,0/0]:[e>0?a[e-1]:n[0],et?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return to(n,t,e)},u()}function eo(n,t){function e(e){return e>=e?t[ta.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return eo(n,t)},e}function ro(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Xi(n,t)},t.tickFormat=function(t,e){return $i(n,t,e)},t.copy=function(){return ro(n)},t}function uo(){return 0}function io(n){return n.innerRadius}function oo(n){return n.outerRadius}function ao(n){return n.startAngle}function co(n){return n.endAngle}function lo(n){return n&&n.padAngle}function so(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function fo(n,t,e,r,u){var i=n[0]-t[0],o=n[1]-t[1],a=(u?r:-r)/Math.sqrt(i*i+o*o),c=a*o,l=-a*i,s=n[0]+c,f=n[1]+l,h=t[0]+c,g=t[1]+l,p=(s+h)/2,v=(f+g)/2,d=h-s,m=g-f,y=d*d+m*m,M=e-r,x=s*g-h*f,b=(0>m?-1:1)*Math.sqrt(M*M*y-x*x),_=(x*m-d*b)/y,w=(-x*d-m*b)/y,S=(x*m+d*b)/y,k=(-x*d+m*b)/y,E=_-p,A=w-v,N=S-p,C=k-v;return E*E+A*A>N*N+C*C&&(_=S,w=k),[[_-c,w-l],[_*e/M,w*e/M]]}function ho(n){function t(t){function o(){l.push("M",i(n(s),a))}for(var c,l=[],s=[],f=-1,h=t.length,g=Et(e),p=Et(r);++f1&&u.push("H",r[0]),u.join("")}function mo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var l=2;l9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function To(n){return n.length<3?go(n):n[0]+_o(n,Lo(n))}function Ro(n){for(var t,e,r,u=-1,i=n.length;++ur)return s();var u=i[i.active];u&&(--i.count,delete i[i.active],u.event&&u.event.interrupt.call(n,n.__data__,u.index)),i.active=r,o.event&&o.event.start.call(n,n.__data__,t),o.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&v.push(r)}),h=o.ease,f=o.duration,ta.timer(function(){return p.c=l(e||1)?Ne:l,1},0,a)}function l(e){if(i.active!==r)return 1;for(var u=e/f,a=h(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,n.__data__,t),s()):void 0}function s(){return--i.count?delete i[r]:delete n[e],1}var f,h,g=o.delay,p=ec,v=[];return p.t=g+a,u>=g?c(u-g):void(p.c=c)},0,a)}}function Bo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function Wo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function Jo(n){return n.toISOString()}function Go(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=ta.bisect(Vl,u);return i==Vl.length?[t.year,Vi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/Vl[i-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=Ko(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Ko(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Pi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Ko(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Go(n.copy(),t,e)},Yi(r,n)}function Ko(n){return new Date(n)}function Qo(n){return JSON.parse(n.responseText)}function na(n){var t=ua.createRange();return t.selectNode(ua.body),t.createContextualFragment(n.responseText)}var ta={version:"3.5.6"},ea=[].slice,ra=function(n){return ea.call(n)},ua=this.document;if(ua)try{ra(ua.documentElement.childNodes)[0].nodeType}catch(ia){ra=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),ua)try{ua.createElement("DIV").style.setProperty("opacity",0,"")}catch(oa){var aa=this.Element.prototype,ca=aa.setAttribute,la=aa.setAttributeNS,sa=this.CSSStyleDeclaration.prototype,fa=sa.setProperty;aa.setAttribute=function(n,t){ca.call(this,n,t+"")},aa.setAttributeNS=function(n,t,e){la.call(this,n,t,e+"")},sa.setProperty=function(n,t,e){fa.call(this,n,t+"",e)}}ta.ascending=e,ta.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},ta.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ur&&(e=r)}else{for(;++u=r){e=r;break}for(;++ur&&(e=r)}return e},ta.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ue&&(e=r)}else{for(;++u=r){e=r;break}for(;++ue&&(e=r)}return e},ta.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}else{for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}return[e,u]},ta.sum=function(n,t){var e,r=0,i=n.length,o=-1;if(1===arguments.length)for(;++o1?c/(s-1):void 0},ta.deviation=function(){var n=ta.variance.apply(this,arguments);return n?Math.sqrt(n):n};var ha=i(e);ta.bisectLeft=ha.left,ta.bisect=ta.bisectRight=ha.right,ta.bisector=function(n){return i(1===n.length?function(t,r){return e(n(t),r)}:n)},ta.shuffle=function(n,t,e){(i=arguments.length)<3&&(e=n.length,2>i&&(t=0));for(var r,u,i=e-t;i;)u=Math.random()*i--|0,r=n[i+t],n[i+t]=n[u+t],n[u+t]=r;return n},ta.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ta.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},ta.zip=function(){if(!(r=arguments.length))return[];for(var n=-1,t=ta.min(arguments,o),e=new Array(t);++n=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var ga=Math.abs;ta.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,u=[],i=a(ga(e)),o=-1;if(n*=i,t*=i,e*=i,0>e)for(;(r=n+e*++o)>t;)u.push(r/i);else for(;(r=n+e*++o)=i.length)return r?r.call(u,o):e?o.sort(e):o;for(var c,s,f,h,g=-1,p=o.length,v=i[a++],d=new l;++g