From 47972cb5c7baba1c78f5ba93020d8c81980f0fe4 Mon Sep 17 00:00:00 2001 From: Lennart Weller Date: Wed, 4 Apr 2018 15:16:20 +0200 Subject: updated missing-sources --- debian/copyright | 27 +- debian/missing-sources/c3-0.4.11.css | 167 - debian/missing-sources/c3-0.4.11.js | 8202 --------- debian/missing-sources/c3-0.4.18.css | 174 + debian/missing-sources/c3-0.4.18.js | 9236 ++++++++++ .../missing-sources/clipboard-polyfill-be05dad.ts | 270 + debian/missing-sources/d3-3.5.17.js | 9554 ----------- debian/missing-sources/d3-4.12.2.js | 17160 +++++++++++++++++++ debian/missing-sources/dygraph-c91c859.js | 3482 ++++ debian/missing-sources/dygraph-combined-dd74404.js | 9392 ---------- .../dygraph-smooth-plotter-c91c859.js | 140 + debian/source/lintian-overrides | 10 +- 12 files changed, 30486 insertions(+), 27328 deletions(-) delete mode 100644 debian/missing-sources/c3-0.4.11.css delete mode 100644 debian/missing-sources/c3-0.4.11.js create mode 100644 debian/missing-sources/c3-0.4.18.css create mode 100644 debian/missing-sources/c3-0.4.18.js create mode 100644 debian/missing-sources/clipboard-polyfill-be05dad.ts delete mode 100644 debian/missing-sources/d3-3.5.17.js create mode 100644 debian/missing-sources/d3-4.12.2.js create mode 100644 debian/missing-sources/dygraph-c91c859.js delete mode 100644 debian/missing-sources/dygraph-combined-dd74404.js create mode 100644 debian/missing-sources/dygraph-smooth-plotter-c91c859.js diff --git a/debian/copyright b/debian/copyright index 6551e2c50..a9491c9a5 100644 --- a/debian/copyright +++ b/debian/copyright @@ -74,17 +74,18 @@ Copyright: 2016 Zhixin Wen License: Expat Files: - web/lib/c3-0.4.11.min.js - web/css/c3-0.4.11.min.css - debian/missing-sources/c3-0.4.11.js - debian/missing-sources/c3-0.4.11.css + web/lib/c3-0.4.18.min.js + web/css/c3-0.4.18.min.css + debian/missing-sources/c3-0.4.18.js + debian/missing-sources/c3-0.4.18.css Copyright: 2013 Masayuki Tanaka License: Expat Files: - web/lib/dygraph-combined-dd74404.js - web/lib/dygraph-smooth-plotter-dd74404.js - debian/missing-sources/dygraph-combined-dd74404.js + web/lib/dygraph-c91c859.min.js + web/lib/dygraph-smooth-plotter-c91c859.js + debian/missing-sources/dygraph-c91c859.js + debian/missing-sources/dygraph-smooth-plotter-c91c859.js Copyright: 2014 Dan Vanderkam License: Expat @@ -133,8 +134,8 @@ Copyright: 2016 JS Foundation License: Apache-2.0 Files: - web/lib/d3-3.5.17.min.js - debian/missing-sources/d3-3.5.17.js + web/lib/d3-4.12.2.min.js + debian/missing-sources/d3-4.12.2.js Copyright: Copyright (C) 2013 Michael Bostock License: BSD-3-Clause @@ -160,6 +161,12 @@ Copyright: 2016 Dmitry Baranovskiy 2016 Sencha Labs License: Expat +Files: + web/lib/clipboard-polyfill-be05dad.js + debian/missing-sources/clipboard-polyfill-be05dad.ts +Copyright: 2014 Lucas Garron +License: Expat + Files: python.d/python_modules/third_party/lm_sensors.py Copyright: 2015 Pavel Rojtberg @@ -176,7 +183,7 @@ Copyright: 2006 Kirill Simonov License: Expat Files: - python.d/python_modules/urllib3 + python.d/python_modules/urllib3/* Copyright: 2008-2016 Andrey Petrov and others License: Expat diff --git a/debian/missing-sources/c3-0.4.11.css b/debian/missing-sources/c3-0.4.11.css deleted file mode 100644 index fda424272..000000000 --- a/debian/missing-sources/c3-0.4.11.css +++ /dev/null @@ -1,167 +0,0 @@ -/*-- Chart --*/ -.c3 svg { - font: 10px sans-serif; - -webkit-tap-highlight-color: transparent; } - -.c3 path, .c3 line { - fill: none; - stroke: #000; } - -.c3 text { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; } - -.c3-legend-item-tile, -.c3-xgrid-focus, -.c3-ygrid, -.c3-event-rect, -.c3-bars path { - shape-rendering: crispEdges; } - -.c3-chart-arc path { - stroke: #fff; } - -.c3-chart-arc text { - fill: #fff; - font-size: 13px; } - -/*-- Axis --*/ -/*-- Grid --*/ -.c3-grid line { - stroke: #aaa; } - -.c3-grid text { - fill: #aaa; } - -.c3-xgrid, .c3-ygrid { - stroke-dasharray: 3 3; } - -/*-- Text on Chart --*/ -.c3-text.c3-empty { - fill: #808080; - font-size: 2em; } - -/*-- Line --*/ -.c3-line { - stroke-width: 1px; } - -/*-- Point --*/ -.c3-circle._expanded_ { - stroke-width: 1px; - stroke: white; } - -.c3-selected-circle { - fill: white; - stroke-width: 2px; } - -/*-- Bar --*/ -.c3-bar { - stroke-width: 0; } - -.c3-bar._expanded_ { - fill-opacity: 0.75; } - -/*-- Focus --*/ -.c3-target.c3-focused { - opacity: 1; } - -.c3-target.c3-focused path.c3-line, .c3-target.c3-focused path.c3-step { - stroke-width: 2px; } - -.c3-target.c3-defocused { - opacity: 0.3 !important; } - -/*-- Region --*/ -.c3-region { - fill: steelblue; - fill-opacity: .1; } - -/*-- Brush --*/ -.c3-brush .extent { - fill-opacity: .1; } - -/*-- Select - Drag --*/ -/*-- Legend --*/ -.c3-legend-item { - font-size: 12px; } - -.c3-legend-item-hidden { - opacity: 0.15; } - -.c3-legend-background { - opacity: 0.75; - fill: white; - stroke: lightgray; - stroke-width: 1; } - -/*-- Title --*/ -.c3-title { - font: 14px sans-serif; } - -/*-- Tooltip --*/ -.c3-tooltip-container { - z-index: 10; } - -.c3-tooltip { - border-collapse: collapse; - border-spacing: 0; - background-color: #fff; - empty-cells: show; - -webkit-box-shadow: 7px 7px 12px -9px #777777; - -moz-box-shadow: 7px 7px 12px -9px #777777; - box-shadow: 7px 7px 12px -9px #777777; - opacity: 0.9; } - -.c3-tooltip tr { - border: 1px solid #CCC; } - -.c3-tooltip th { - background-color: #aaa; - font-size: 14px; - padding: 2px 5px; - text-align: left; - color: #FFF; } - -.c3-tooltip td { - font-size: 13px; - padding: 3px 6px; - background-color: #fff; - border-left: 1px dotted #999; } - -.c3-tooltip td > span { - display: inline-block; - width: 10px; - height: 10px; - margin-right: 6px; } - -.c3-tooltip td.value { - text-align: right; } - -/*-- Area --*/ -.c3-area { - stroke-width: 0; - opacity: 0.2; } - -/*-- Arc --*/ -.c3-chart-arcs-title { - dominant-baseline: middle; - font-size: 1.3em; } - -.c3-chart-arcs .c3-chart-arcs-background { - fill: #e0e0e0; - stroke: none; } - -.c3-chart-arcs .c3-chart-arcs-gauge-unit { - fill: #000; - font-size: 16px; } - -.c3-chart-arcs .c3-chart-arcs-gauge-max { - fill: #777; } - -.c3-chart-arcs .c3-chart-arcs-gauge-min { - fill: #777; } - -.c3-chart-arc .c3-gauge-value { - fill: #000; - /* font-size: 28px !important;*/ } diff --git a/debian/missing-sources/c3-0.4.11.js b/debian/missing-sources/c3-0.4.11.js deleted file mode 100644 index 2ce7f7e0f..000000000 --- a/debian/missing-sources/c3-0.4.11.js +++ /dev/null @@ -1,8202 +0,0 @@ -(function (window) { - 'use strict'; - - /*global define, module, exports, require */ - - var c3 = { version: "0.4.11" }; - - var c3_chart_fn, - c3_chart_internal_fn, - c3_chart_internal_axis_fn; - - function API(owner) { - this.owner = owner; - } - - function inherit(base, derived) { - - if (Object.create) { - derived.prototype = Object.create(base.prototype); - } else { - var f = function f() {}; - f.prototype = base.prototype; - derived.prototype = new f(); - } - - derived.prototype.constructor = derived; - - return derived; - } - - function Chart(config) { - var $$ = this.internal = new ChartInternal(this); - $$.loadConfig(config); - - $$.beforeInit(config); - $$.init(); - $$.afterInit(config); - - // bind "this" to nested API - (function bindThis(fn, target, argThis) { - Object.keys(fn).forEach(function (key) { - target[key] = fn[key].bind(argThis); - if (Object.keys(fn[key]).length > 0) { - bindThis(fn[key], target[key], argThis); - } - }); - })(c3_chart_fn, this, this); - } - - function ChartInternal(api) { - var $$ = this; - $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require("d3") : undefined; - $$.api = api; - $$.config = $$.getDefaultConfig(); - $$.data = {}; - $$.cache = {}; - $$.axes = {}; - } - - c3.generate = function (config) { - return new Chart(config); - }; - - c3.chart = { - fn: Chart.prototype, - internal: { - fn: ChartInternal.prototype, - axis: { - fn: Axis.prototype - } - } - }; - c3_chart_fn = c3.chart.fn; - c3_chart_internal_fn = c3.chart.internal.fn; - c3_chart_internal_axis_fn = c3.chart.internal.axis.fn; - - c3_chart_internal_fn.beforeInit = function () { - // can do something - }; - c3_chart_internal_fn.afterInit = function () { - // can do something - }; - c3_chart_internal_fn.init = function () { - var $$ = this, config = $$.config; - - $$.initParams(); - - if (config.data_url) { - $$.convertUrlToData(config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData); - } - else if (config.data_json) { - $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys)); - } - else if (config.data_rows) { - $$.initWithData($$.convertRowsToData(config.data_rows)); - } - else if (config.data_columns) { - $$.initWithData($$.convertColumnsToData(config.data_columns)); - } - else { - throw Error('url or json or rows or columns is required.'); - } - }; - - c3_chart_internal_fn.initParams = function () { - var $$ = this, d3 = $$.d3, config = $$.config; - - // MEMO: clipId needs to be unique because it conflicts when multiple charts exist - $$.clipId = "c3-" + (+new Date()) + '-clip', - $$.clipIdForXAxis = $$.clipId + '-xaxis', - $$.clipIdForYAxis = $$.clipId + '-yaxis', - $$.clipIdForGrid = $$.clipId + '-grid', - $$.clipIdForSubchart = $$.clipId + '-subchart', - $$.clipPath = $$.getClipPath($$.clipId), - $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis), - $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis); - $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid), - $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart), - - $$.dragStart = null; - $$.dragging = false; - $$.flowing = false; - $$.cancelClick = false; - $$.mouseover = false; - $$.transiting = false; - - $$.color = $$.generateColor(); - $$.levelColor = $$.generateLevelColor(); - - $$.dataTimeFormat = config.data_xLocaltime ? d3.time.format : d3.time.format.utc; - $$.axisTimeFormat = config.axis_x_localtime ? d3.time.format : d3.time.format.utc; - $$.defaultAxisTimeFormat = $$.axisTimeFormat.multi([ - [".%L", function (d) { return d.getMilliseconds(); }], - [":%S", function (d) { return d.getSeconds(); }], - ["%I:%M", function (d) { return d.getMinutes(); }], - ["%I %p", function (d) { return d.getHours(); }], - ["%-m/%-d", function (d) { return d.getDay() && d.getDate() !== 1; }], - ["%-m/%-d", function (d) { return d.getDate() !== 1; }], - ["%-m/%-d", function (d) { return d.getMonth(); }], - ["%Y/%-m/%-d", function () { return true; }] - ]); - - $$.hiddenTargetIds = []; - $$.hiddenLegendIds = []; - $$.focusedTargetIds = []; - $$.defocusedTargetIds = []; - - $$.xOrient = config.axis_rotated ? "left" : "bottom"; - $$.yOrient = config.axis_rotated ? (config.axis_y_inner ? "top" : "bottom") : (config.axis_y_inner ? "right" : "left"); - $$.y2Orient = config.axis_rotated ? (config.axis_y2_inner ? "bottom" : "top") : (config.axis_y2_inner ? "left" : "right"); - $$.subXOrient = config.axis_rotated ? "left" : "bottom"; - - $$.isLegendRight = config.legend_position === 'right'; - $$.isLegendInset = config.legend_position === 'inset'; - $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right'; - $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left'; - $$.legendStep = 0; - $$.legendItemWidth = 0; - $$.legendItemHeight = 0; - - $$.currentMaxTickWidths = { - x: 0, - y: 0, - y2: 0 - }; - - $$.rotated_padding_left = 30; - $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30; - $$.rotated_padding_top = 5; - - $$.withoutFadeIn = {}; - - $$.intervalForObserveInserted = undefined; - - $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js - }; - - c3_chart_internal_fn.initChartElements = function () { - if (this.initBar) { this.initBar(); } - if (this.initLine) { this.initLine(); } - if (this.initArc) { this.initArc(); } - if (this.initGauge) { this.initGauge(); } - if (this.initText) { this.initText(); } - }; - - c3_chart_internal_fn.initWithData = function (data) { - var $$ = this, d3 = $$.d3, config = $$.config; - var defs, main, binding = true; - - $$.axis = new Axis($$); - - if ($$.initPie) { $$.initPie(); } - if ($$.initBrush) { $$.initBrush(); } - if ($$.initZoom) { $$.initZoom(); } - - if (!config.bindto) { - $$.selectChart = d3.selectAll([]); - } - else if (typeof config.bindto.node === 'function') { - $$.selectChart = config.bindto; - } - else { - $$.selectChart = d3.select(config.bindto); - } - if ($$.selectChart.empty()) { - $$.selectChart = d3.select(document.createElement('div')).style('opacity', 0); - $$.observeInserted($$.selectChart); - binding = false; - } - $$.selectChart.html("").classed("c3", true); - - // Init data as targets - $$.data.xs = {}; - $$.data.targets = $$.convertDataToTargets(data); - - if (config.data_filter) { - $$.data.targets = $$.data.targets.filter(config.data_filter); - } - - // Set targets to hide if needed - if (config.data_hide) { - $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide); - } - if (config.legend_hide) { - $$.addHiddenLegendIds(config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide); - } - - // when gauge, hide legend // TODO: fix - if ($$.hasType('gauge')) { - config.legend_show = false; - } - - // Init sizes and scales - $$.updateSizes(); - $$.updateScales(); - - // Set domains for each scale - $$.x.domain(d3.extent($$.getXDomain($$.data.targets))); - $$.y.domain($$.getYDomain($$.data.targets, 'y')); - $$.y2.domain($$.getYDomain($$.data.targets, 'y2')); - $$.subX.domain($$.x.domain()); - $$.subY.domain($$.y.domain()); - $$.subY2.domain($$.y2.domain()); - - // Save original x domain for zoom update - $$.orgXDomain = $$.x.domain(); - - // Set initialized scales to brush and zoom - if ($$.brush) { $$.brush.scale($$.subX); } - if (config.zoom_enabled) { $$.zoom.scale($$.x); } - - /*-- Basic Elements --*/ - - // Define svgs - $$.svg = $$.selectChart.append("svg") - .style("overflow", "hidden") - .on('mouseenter', function () { return config.onmouseover.call($$); }) - .on('mouseleave', function () { return config.onmouseout.call($$); }); - - if ($$.config.svg_classname) { - $$.svg.attr('class', $$.config.svg_classname); - } - - // Define defs - defs = $$.svg.append("defs"); - $$.clipChart = $$.appendClip(defs, $$.clipId); - $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis); - $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis); - $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid); - $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart); - $$.updateSvgSize(); - - // Define regions - main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main')); - - if ($$.initSubchart) { $$.initSubchart(); } - if ($$.initTooltip) { $$.initTooltip(); } - if ($$.initLegend) { $$.initLegend(); } - if ($$.initTitle) { $$.initTitle(); } - - /*-- Main Region --*/ - - // text when empty - main.append("text") - .attr("class", CLASS.text + ' ' + CLASS.empty) - .attr("text-anchor", "middle") // horizontal centering of text at x position in all browsers. - .attr("dominant-baseline", "middle"); // vertical centering of text at y position in all browsers, except IE. - - // Regions - $$.initRegion(); - - // Grids - $$.initGrid(); - - // Define g for chart area - main.append('g') - .attr("clip-path", $$.clipPath) - .attr('class', CLASS.chart); - - // Grid lines - if (config.grid_lines_front) { $$.initGridLines(); } - - // Cover whole with rects for events - $$.initEventRect(); - - // Define g for chart - $$.initChartElements(); - - // if zoom privileged, insert rect to forefront - // TODO: is this needed? - main.insert('rect', config.zoom_privileged ? null : 'g.' + CLASS.regions) - .attr('class', CLASS.zoomRect) - .attr('width', $$.width) - .attr('height', $$.height) - .style('opacity', 0) - .on("dblclick.zoom", null); - - // Set default extent if defined - if (config.axis_x_extent) { $$.brush.extent($$.getDefaultExtent()); } - - // Add Axis - $$.axis.init(); - - // Set targets - $$.updateTargets($$.data.targets); - - // Draw with targets - if (binding) { - $$.updateDimension(); - $$.config.oninit.call($$); - $$.redraw({ - withTransition: false, - withTransform: true, - withUpdateXDomain: true, - withUpdateOrgXDomain: true, - withTransitionForAxis: false - }); - } - - // Bind resize event - $$.bindResize(); - - // export element of the chart - $$.api.element = $$.selectChart.node(); - }; - - c3_chart_internal_fn.smoothLines = function (el, type) { - var $$ = this; - if (type === 'grid') { - el.each(function () { - var g = $$.d3.select(this), - x1 = g.attr('x1'), - x2 = g.attr('x2'), - y1 = g.attr('y1'), - y2 = g.attr('y2'); - g.attr({ - 'x1': Math.ceil(x1), - 'x2': Math.ceil(x2), - 'y1': Math.ceil(y1), - 'y2': Math.ceil(y2) - }); - }); - } - }; - - - c3_chart_internal_fn.updateSizes = function () { - var $$ = this, config = $$.config; - var legendHeight = $$.legend ? $$.getLegendHeight() : 0, - legendWidth = $$.legend ? $$.getLegendWidth() : 0, - legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight, - hasArc = $$.hasArcType(), - xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'), - subchartHeight = config.subchart_show && !hasArc ? (config.subchart_size_height + xAxisHeight) : 0; - - $$.currentWidth = $$.getCurrentWidth(); - $$.currentHeight = $$.getCurrentHeight(); - - // for main - $$.margin = config.axis_rotated ? { - top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(), - right: hasArc ? 0 : $$.getCurrentPaddingRight(), - bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(), - left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft()) - } : { - top: 4 + $$.getCurrentPaddingTop(), // for top tick text - right: hasArc ? 0 : $$.getCurrentPaddingRight(), - bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(), - left: hasArc ? 0 : $$.getCurrentPaddingLeft() - }; - - // for subchart - $$.margin2 = config.axis_rotated ? { - top: $$.margin.top, - right: NaN, - bottom: 20 + legendHeightForBottom, - left: $$.rotated_padding_left - } : { - top: $$.currentHeight - subchartHeight - legendHeightForBottom, - right: NaN, - bottom: xAxisHeight + legendHeightForBottom, - left: $$.margin.left - }; - - // for legend - $$.margin3 = { - top: 0, - right: NaN, - bottom: 0, - left: 0 - }; - if ($$.updateSizeForLegend) { $$.updateSizeForLegend(legendHeight, legendWidth); } - - $$.width = $$.currentWidth - $$.margin.left - $$.margin.right; - $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom; - if ($$.width < 0) { $$.width = 0; } - if ($$.height < 0) { $$.height = 0; } - - $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width; - $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom; - if ($$.width2 < 0) { $$.width2 = 0; } - if ($$.height2 < 0) { $$.height2 = 0; } - - // for arc - $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0); - $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10); - if ($$.hasType('gauge') && !config.gauge_fullCircle) { - $$.arcHeight += $$.height - $$.getGaugeLabelHeight(); - } - if ($$.updateRadius) { $$.updateRadius(); } - - if ($$.isLegendRight && hasArc) { - $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1; - } - }; - - c3_chart_internal_fn.updateTargets = function (targets) { - var $$ = this; - - /*-- Main --*/ - - //-- Text --// - $$.updateTargetsForText(targets); - - //-- Bar --// - $$.updateTargetsForBar(targets); - - //-- Line --// - $$.updateTargetsForLine(targets); - - //-- Arc --// - if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets); } - - /*-- Sub --*/ - - if ($$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); } - - // Fade-in each chart - $$.showTargets(); - }; - c3_chart_internal_fn.showTargets = function () { - var $$ = this; - $$.svg.selectAll('.' + CLASS.target).filter(function (d) { return $$.isTargetToShow(d.id); }) - .transition().duration($$.config.transition_duration) - .style("opacity", 1); - }; - - c3_chart_internal_fn.redraw = function (options, transitions) { - var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config; - var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType); - var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, - withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, - withEventRect, withDimension, withUpdateXAxis; - var hideAxis = $$.hasArcType(); - var drawArea, drawBar, drawLine, xForText, yForText; - var duration, durationForExit, durationForAxis; - var waitForDraw, flow; - var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom; - var xv = $$.xv.bind($$), cx, cy; - - options = options || {}; - withY = getOption(options, "withY", true); - withSubchart = getOption(options, "withSubchart", true); - withTransition = getOption(options, "withTransition", true); - withTransform = getOption(options, "withTransform", false); - withUpdateXDomain = getOption(options, "withUpdateXDomain", false); - withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", false); - withTrimXDomain = getOption(options, "withTrimXDomain", true); - withUpdateXAxis = getOption(options, "withUpdateXAxis", withUpdateXDomain); - withLegend = getOption(options, "withLegend", false); - withEventRect = getOption(options, "withEventRect", true); - withDimension = getOption(options, "withDimension", true); - withTransitionForExit = getOption(options, "withTransitionForExit", withTransition); - withTransitionForAxis = getOption(options, "withTransitionForAxis", withTransition); - - duration = withTransition ? config.transition_duration : 0; - durationForExit = withTransitionForExit ? duration : 0; - durationForAxis = withTransitionForAxis ? duration : 0; - - transitions = transitions || $$.axis.generateTransitions(durationForAxis); - - // update legend and transform each g - if (withLegend && config.legend_show) { - $$.updateLegend($$.mapToIds($$.data.targets), options, transitions); - } else if (withDimension) { - // need to update dimension (e.g. axis.y.tick.values) because y tick values should change - // no need to update axis in it because they will be updated in redraw() - $$.updateDimension(true); - } - - // MEMO: needed for grids calculation - if ($$.isCategorized() && targetsToShow.length === 0) { - $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]); - } - - if (targetsToShow.length) { - $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain); - if (!config.axis_x_tick_values) { - tickValues = $$.axis.updateXAxisTickValues(targetsToShow); - } - } else { - $$.xAxis.tickValues([]); - $$.subXAxis.tickValues([]); - } - - if (config.zoom_rescale && !options.flow) { - xDomainForZoom = $$.x.orgDomain(); - } - - $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom)); - $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom)); - - if (!config.axis_y_tick_values && config.axis_y_tick_count) { - $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count)); - } - if (!config.axis_y2_tick_values && config.axis_y2_tick_count) { - $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count)); - } - - // axes - $$.axis.redraw(transitions, hideAxis); - - // Update axis label - $$.axis.updateLabels(withTransition); - - // show/hide if manual culling needed - if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) { - if (config.axis_x_tick_culling && tickValues) { - for (i = 1; i < tickValues.length; i++) { - if (tickValues.length / i < config.axis_x_tick_culling_max) { - intervalForCulling = i; - break; - } - } - $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) { - var index = tickValues.indexOf(e); - if (index >= 0) { - d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block'); - } - }); - } else { - $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').style('display', 'block'); - } - } - - // setup drawer - MEMO: these must be called after axis updated - drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined; - drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined; - drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined; - xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true); - yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false); - - // Update sub domain - if (withY) { - $$.subY.domain($$.getYDomain(targetsToShow, 'y')); - $$.subY2.domain($$.getYDomain(targetsToShow, 'y2')); - } - - // xgrid focus - $$.updateXgridFocus(); - - // Data empty label positioning and text. - main.select("text." + CLASS.text + '.' + CLASS.empty) - .attr("x", $$.width / 2) - .attr("y", $$.height / 2) - .text(config.data_empty_label_text) - .transition() - .style('opacity', targetsToShow.length ? 0 : 1); - - // grid - $$.updateGrid(duration); - - // rect for regions - $$.updateRegion(duration); - - // bars - $$.updateBar(durationForExit); - - // lines, areas and cricles - $$.updateLine(durationForExit); - $$.updateArea(durationForExit); - $$.updateCircle(); - - // text - if ($$.hasDataLabel()) { - $$.updateText(durationForExit); - } - - // title - if ($$.redrawTitle) { $$.redrawTitle(); } - - // arc - if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); } - - // subchart - if ($$.redrawSubchart) { - $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices); - } - - // circles for select - main.selectAll('.' + CLASS.selectedCircles) - .filter($$.isBarType.bind($$)) - .selectAll('circle') - .remove(); - - // event rects will redrawn when flow called - if (config.interaction_enabled && !options.flow && withEventRect) { - $$.redrawEventRect(); - if ($$.updateZoom) { $$.updateZoom(); } - } - - // update circleY based on updated parameters - $$.updateCircleY(); - - // generate circle x/y functions depending on updated params - cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$); - cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$); - - if (options.flow) { - flow = $$.generateFlow({ - targets: targetsToShow, - flow: options.flow, - duration: options.flow.duration, - drawBar: drawBar, - drawLine: drawLine, - drawArea: drawArea, - cx: cx, - cy: cy, - xv: xv, - xForText: xForText, - yForText: yForText - }); - } - - if ((duration || flow) && $$.isTabVisible()) { // Only use transition if tab visible. See #938. - // transition should be derived from one transition - d3.transition().duration(duration).each(function () { - var transitionsToWait = []; - - // redraw and gather transitions - [ - $$.redrawBar(drawBar, true), - $$.redrawLine(drawLine, true), - $$.redrawArea(drawArea, true), - $$.redrawCircle(cx, cy, true), - $$.redrawText(xForText, yForText, options.flow, true), - $$.redrawRegion(true), - $$.redrawGrid(true), - ].forEach(function (transitions) { - transitions.forEach(function (transition) { - transitionsToWait.push(transition); - }); - }); - - // Wait for end of transitions to call flow and onrendered callback - waitForDraw = $$.generateWait(); - transitionsToWait.forEach(function (t) { - waitForDraw.add(t); - }); - }) - .call(waitForDraw, function () { - if (flow) { - flow(); - } - if (config.onrendered) { - config.onrendered.call($$); - } - }); - } - else { - $$.redrawBar(drawBar); - $$.redrawLine(drawLine); - $$.redrawArea(drawArea); - $$.redrawCircle(cx, cy); - $$.redrawText(xForText, yForText, options.flow); - $$.redrawRegion(); - $$.redrawGrid(); - if (config.onrendered) { - config.onrendered.call($$); - } - } - - // update fadein condition - $$.mapToIds($$.data.targets).forEach(function (id) { - $$.withoutFadeIn[id] = true; - }); - }; - - c3_chart_internal_fn.updateAndRedraw = function (options) { - var $$ = this, config = $$.config, transitions; - options = options || {}; - // same with redraw - options.withTransition = getOption(options, "withTransition", true); - options.withTransform = getOption(options, "withTransform", false); - options.withLegend = getOption(options, "withLegend", false); - // NOT same with redraw - options.withUpdateXDomain = true; - options.withUpdateOrgXDomain = true; - options.withTransitionForExit = false; - options.withTransitionForTransform = getOption(options, "withTransitionForTransform", options.withTransition); - // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called) - $$.updateSizes(); - // MEMO: called in updateLegend in redraw if withLegend - if (!(options.withLegend && config.legend_show)) { - transitions = $$.axis.generateTransitions(options.withTransitionForAxis ? config.transition_duration : 0); - // Update scales - $$.updateScales(); - $$.updateSvgSize(); - // Update g positions - $$.transformAll(options.withTransitionForTransform, transitions); - } - // Draw with new sizes & scales - $$.redraw(options, transitions); - }; - c3_chart_internal_fn.redrawWithoutRescale = function () { - this.redraw({ - withY: false, - withSubchart: false, - withEventRect: false, - withTransitionForAxis: false - }); - }; - - c3_chart_internal_fn.isTimeSeries = function () { - return this.config.axis_x_type === 'timeseries'; - }; - c3_chart_internal_fn.isCategorized = function () { - return this.config.axis_x_type.indexOf('categor') >= 0; - }; - c3_chart_internal_fn.isCustomX = function () { - var $$ = this, config = $$.config; - return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs)); - }; - - c3_chart_internal_fn.isTimeSeriesY = function () { - return this.config.axis_y_type === 'timeseries'; - }; - - c3_chart_internal_fn.getTranslate = function (target) { - var $$ = this, config = $$.config, x, y; - if (target === 'main') { - x = asHalfPixel($$.margin.left); - y = asHalfPixel($$.margin.top); - } else if (target === 'context') { - x = asHalfPixel($$.margin2.left); - y = asHalfPixel($$.margin2.top); - } else if (target === 'legend') { - x = $$.margin3.left; - y = $$.margin3.top; - } else if (target === 'x') { - x = 0; - y = config.axis_rotated ? 0 : $$.height; - } else if (target === 'y') { - x = 0; - y = config.axis_rotated ? $$.height : 0; - } else if (target === 'y2') { - x = config.axis_rotated ? 0 : $$.width; - y = config.axis_rotated ? 1 : 0; - } else if (target === 'subx') { - x = 0; - y = config.axis_rotated ? 0 : $$.height2; - } else if (target === 'arc') { - x = $$.arcWidth / 2; - y = $$.arcHeight / 2; - } - return "translate(" + x + "," + y + ")"; - }; - c3_chart_internal_fn.initialOpacity = function (d) { - return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0; - }; - c3_chart_internal_fn.initialOpacityForCircle = function (d) { - return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0; - }; - c3_chart_internal_fn.opacityForCircle = function (d) { - var opacity = this.config.point_show ? 1 : 0; - return isValue(d.value) ? (this.isScatterType(d) ? 0.5 : opacity) : 0; - }; - c3_chart_internal_fn.opacityForText = function () { - return this.hasDataLabel() ? 1 : 0; - }; - c3_chart_internal_fn.xx = function (d) { - return d ? this.x(d.x) : null; - }; - c3_chart_internal_fn.xv = function (d) { - var $$ = this, value = d.value; - if ($$.isTimeSeries()) { - value = $$.parseDate(d.value); - } - else if ($$.isCategorized() && typeof d.value === 'string') { - value = $$.config.axis_x_categories.indexOf(d.value); - } - return Math.ceil($$.x(value)); - }; - c3_chart_internal_fn.yv = function (d) { - var $$ = this, - yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y; - return Math.ceil(yScale(d.value)); - }; - c3_chart_internal_fn.subxx = function (d) { - return d ? this.subX(d.x) : null; - }; - - c3_chart_internal_fn.transformMain = function (withTransition, transitions) { - var $$ = this, - xAxis, yAxis, y2Axis; - if (transitions && transitions.axisX) { - xAxis = transitions.axisX; - } else { - xAxis = $$.main.select('.' + CLASS.axisX); - if (withTransition) { xAxis = xAxis.transition(); } - } - if (transitions && transitions.axisY) { - yAxis = transitions.axisY; - } else { - yAxis = $$.main.select('.' + CLASS.axisY); - if (withTransition) { yAxis = yAxis.transition(); } - } - if (transitions && transitions.axisY2) { - y2Axis = transitions.axisY2; - } else { - y2Axis = $$.main.select('.' + CLASS.axisY2); - if (withTransition) { y2Axis = y2Axis.transition(); } - } - (withTransition ? $$.main.transition() : $$.main).attr("transform", $$.getTranslate('main')); - xAxis.attr("transform", $$.getTranslate('x')); - yAxis.attr("transform", $$.getTranslate('y')); - y2Axis.attr("transform", $$.getTranslate('y2')); - $$.main.select('.' + CLASS.chartArcs).attr("transform", $$.getTranslate('arc')); - }; - c3_chart_internal_fn.transformAll = function (withTransition, transitions) { - var $$ = this; - $$.transformMain(withTransition, transitions); - if ($$.config.subchart_show) { $$.transformContext(withTransition, transitions); } - if ($$.legend) { $$.transformLegend(withTransition); } - }; - - c3_chart_internal_fn.updateSvgSize = function () { - var $$ = this, - brush = $$.svg.select(".c3-brush .background"); - $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight); - $$.svg.selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]).select('rect') - .attr('width', $$.width) - .attr('height', $$.height); - $$.svg.select('#' + $$.clipIdForXAxis).select('rect') - .attr('x', $$.getXAxisClipX.bind($$)) - .attr('y', $$.getXAxisClipY.bind($$)) - .attr('width', $$.getXAxisClipWidth.bind($$)) - .attr('height', $$.getXAxisClipHeight.bind($$)); - $$.svg.select('#' + $$.clipIdForYAxis).select('rect') - .attr('x', $$.getYAxisClipX.bind($$)) - .attr('y', $$.getYAxisClipY.bind($$)) - .attr('width', $$.getYAxisClipWidth.bind($$)) - .attr('height', $$.getYAxisClipHeight.bind($$)); - $$.svg.select('#' + $$.clipIdForSubchart).select('rect') - .attr('width', $$.width) - .attr('height', brush.size() ? brush.attr('height') : 0); - $$.svg.select('.' + CLASS.zoomRect) - .attr('width', $$.width) - .attr('height', $$.height); - // MEMO: parent div's height will be bigger than svg when - $$.selectChart.style('max-height', $$.currentHeight + "px"); - }; - - - c3_chart_internal_fn.updateDimension = function (withoutAxis) { - var $$ = this; - if (!withoutAxis) { - if ($$.config.axis_rotated) { - $$.axes.x.call($$.xAxis); - $$.axes.subx.call($$.subXAxis); - } else { - $$.axes.y.call($$.yAxis); - $$.axes.y2.call($$.y2Axis); - } - } - $$.updateSizes(); - $$.updateScales(); - $$.updateSvgSize(); - $$.transformAll(false); - }; - - c3_chart_internal_fn.observeInserted = function (selection) { - var $$ = this, observer; - if (typeof MutationObserver === 'undefined') { - window.console.error("MutationObserver not defined."); - return; - } - observer= new MutationObserver(function (mutations) { - mutations.forEach(function (mutation) { - if (mutation.type === 'childList' && mutation.previousSibling) { - observer.disconnect(); - // need to wait for completion of load because size calculation requires the actual sizes determined after that completion - $$.intervalForObserveInserted = window.setInterval(function () { - // parentNode will NOT be null when completed - if (selection.node().parentNode) { - window.clearInterval($$.intervalForObserveInserted); - $$.updateDimension(); - if ($$.brush) { $$.brush.update(); } - $$.config.oninit.call($$); - $$.redraw({ - withTransform: true, - withUpdateXDomain: true, - withUpdateOrgXDomain: true, - withTransition: false, - withTransitionForTransform: false, - withLegend: true - }); - selection.transition().style('opacity', 1); - } - }, 10); - } - }); - }); - observer.observe(selection.node(), {attributes: true, childList: true, characterData: true}); - }; - - c3_chart_internal_fn.bindResize = function () { - var $$ = this, config = $$.config; - - $$.resizeFunction = $$.generateResize(); - - $$.resizeFunction.add(function () { - config.onresize.call($$); - }); - if (config.resize_auto) { - $$.resizeFunction.add(function () { - if ($$.resizeTimeout !== undefined) { - window.clearTimeout($$.resizeTimeout); - } - $$.resizeTimeout = window.setTimeout(function () { - delete $$.resizeTimeout; - $$.api.flush(); - }, 100); - }); - } - $$.resizeFunction.add(function () { - config.onresized.call($$); - }); - - if (window.attachEvent) { - window.attachEvent('onresize', $$.resizeFunction); - } else if (window.addEventListener) { - window.addEventListener('resize', $$.resizeFunction, false); - } else { - // fallback to this, if this is a very old browser - var wrapper = window.onresize; - if (!wrapper) { - // create a wrapper that will call all charts - wrapper = $$.generateResize(); - } else if (!wrapper.add || !wrapper.remove) { - // there is already a handler registered, make sure we call it too - wrapper = $$.generateResize(); - wrapper.add(window.onresize); - } - // add this graph to the wrapper, we will be removed if the user calls destroy - wrapper.add($$.resizeFunction); - window.onresize = wrapper; - } - }; - - c3_chart_internal_fn.generateResize = function () { - var resizeFunctions = []; - function callResizeFunctions() { - resizeFunctions.forEach(function (f) { - f(); - }); - } - callResizeFunctions.add = function (f) { - resizeFunctions.push(f); - }; - callResizeFunctions.remove = function (f) { - for (var i = 0; i < resizeFunctions.length; i++) { - if (resizeFunctions[i] === f) { - resizeFunctions.splice(i, 1); - break; - } - } - }; - return callResizeFunctions; - }; - - c3_chart_internal_fn.endall = function (transition, callback) { - var n = 0; - transition - .each(function () { ++n; }) - .each("end", function () { - if (!--n) { callback.apply(this, arguments); } - }); - }; - c3_chart_internal_fn.generateWait = function () { - var transitionsToWait = [], - f = function (transition, callback) { - var timer = setInterval(function () { - var done = 0; - transitionsToWait.forEach(function (t) { - if (t.empty()) { - done += 1; - return; - } - try { - t.transition(); - } catch (e) { - done += 1; - } - }); - if (done === transitionsToWait.length) { - clearInterval(timer); - if (callback) { callback(); } - } - }, 10); - }; - f.add = function (transition) { - transitionsToWait.push(transition); - }; - return f; - }; - - c3_chart_internal_fn.parseDate = function (date) { - var $$ = this, parsedDate; - if (date instanceof Date) { - parsedDate = date; - } else if (typeof date === 'string') { - parsedDate = $$.dataTimeFormat($$.config.data_xFormat).parse(date); - } else if (typeof date === 'number' && !isNaN(date)) { - parsedDate = new Date(+date); - } - if (!parsedDate || isNaN(+parsedDate)) { - window.console.error("Failed to parse x '" + date + "' to Date object"); - } - return parsedDate; - }; - - c3_chart_internal_fn.isTabVisible = function () { - var hidden; - if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support - hidden = "hidden"; - } else if (typeof document.mozHidden !== "undefined") { - hidden = "mozHidden"; - } else if (typeof document.msHidden !== "undefined") { - hidden = "msHidden"; - } else if (typeof document.webkitHidden !== "undefined") { - hidden = "webkitHidden"; - } - - return document[hidden] ? false : true; - }; - - c3_chart_internal_fn.getDefaultConfig = function () { - var config = { - bindto: '#chart', - svg_classname: undefined, - size_width: undefined, - size_height: undefined, - padding_left: undefined, - padding_right: undefined, - padding_top: undefined, - padding_bottom: undefined, - resize_auto: true, - zoom_enabled: false, - zoom_extent: undefined, - zoom_privileged: false, - zoom_rescale: false, - zoom_onzoom: function () {}, - zoom_onzoomstart: function () {}, - zoom_onzoomend: function () {}, - zoom_x_min: undefined, - zoom_x_max: undefined, - interaction_brighten: true, - interaction_enabled: true, - onmouseover: function () {}, - onmouseout: function () {}, - onresize: function () {}, - onresized: function () {}, - oninit: function () {}, - onrendered: function () {}, - transition_duration: 350, - data_x: undefined, - data_xs: {}, - data_xFormat: '%Y-%m-%d', - data_xLocaltime: true, - data_xSort: true, - data_idConverter: function (id) { return id; }, - data_names: {}, - data_classes: {}, - data_groups: [], - data_axes: {}, - data_type: undefined, - data_types: {}, - data_labels: {}, - data_order: 'desc', - data_regions: {}, - data_color: undefined, - data_colors: {}, - data_hide: false, - data_filter: undefined, - data_selection_enabled: false, - data_selection_grouped: false, - data_selection_isselectable: function () { return true; }, - data_selection_multiple: true, - data_selection_draggable: false, - data_onclick: function () {}, - data_onmouseover: function () {}, - data_onmouseout: function () {}, - data_onselected: function () {}, - data_onunselected: function () {}, - data_url: undefined, - data_headers: undefined, - data_json: undefined, - data_rows: undefined, - data_columns: undefined, - data_mimeType: undefined, - data_keys: undefined, - // configuration for no plot-able data supplied. - data_empty_label_text: "", - // subchart - subchart_show: false, - subchart_size_height: 60, - subchart_axis_x_show: true, - subchart_onbrush: function () {}, - // color - color_pattern: [], - color_threshold: {}, - // legend - legend_show: true, - legend_hide: false, - legend_position: 'bottom', - legend_inset_anchor: 'top-left', - legend_inset_x: 10, - legend_inset_y: 0, - legend_inset_step: undefined, - legend_item_onclick: undefined, - legend_item_onmouseover: undefined, - legend_item_onmouseout: undefined, - legend_equally: false, - legend_padding: 0, - legend_item_tile_width: 10, - legend_item_tile_height: 10, - // axis - axis_rotated: false, - axis_x_show: true, - axis_x_type: 'indexed', - axis_x_localtime: true, - axis_x_categories: [], - axis_x_tick_centered: false, - axis_x_tick_format: undefined, - axis_x_tick_culling: {}, - axis_x_tick_culling_max: 10, - axis_x_tick_count: undefined, - axis_x_tick_fit: true, - axis_x_tick_values: null, - axis_x_tick_rotate: 0, - axis_x_tick_outer: true, - axis_x_tick_multiline: true, - axis_x_tick_width: null, - axis_x_max: undefined, - axis_x_min: undefined, - axis_x_padding: {}, - axis_x_height: undefined, - axis_x_extent: undefined, - axis_x_label: {}, - axis_y_show: true, - axis_y_type: undefined, - axis_y_max: undefined, - axis_y_min: undefined, - axis_y_inverted: false, - axis_y_center: undefined, - axis_y_inner: undefined, - axis_y_label: {}, - axis_y_tick_format: undefined, - axis_y_tick_outer: true, - axis_y_tick_values: null, - axis_y_tick_rotate: 0, - axis_y_tick_count: undefined, - axis_y_tick_time_value: undefined, - axis_y_tick_time_interval: undefined, - axis_y_padding: {}, - axis_y_default: undefined, - axis_y2_show: false, - axis_y2_max: undefined, - axis_y2_min: undefined, - axis_y2_inverted: false, - axis_y2_center: undefined, - axis_y2_inner: undefined, - axis_y2_label: {}, - axis_y2_tick_format: undefined, - axis_y2_tick_outer: true, - axis_y2_tick_values: null, - axis_y2_tick_count: undefined, - axis_y2_padding: {}, - axis_y2_default: undefined, - // grid - grid_x_show: false, - grid_x_type: 'tick', - grid_x_lines: [], - grid_y_show: false, - // not used - // grid_y_type: 'tick', - grid_y_lines: [], - grid_y_ticks: 10, - grid_focus_show: true, - grid_lines_front: true, - // point - point of each data - point_show: true, - point_r: 2.5, - point_sensitivity: 10, - point_focus_expand_enabled: true, - point_focus_expand_r: undefined, - point_select_r: undefined, - // line - line_connectNull: false, - line_step_type: 'step', - // bar - bar_width: undefined, - bar_width_ratio: 0.6, - bar_width_max: undefined, - bar_zerobased: true, - // area - area_zerobased: true, - area_above: false, - // pie - pie_label_show: true, - pie_label_format: undefined, - pie_label_threshold: 0.05, - pie_label_ratio: undefined, - pie_expand: {}, - pie_expand_duration: 50, - // gauge - gauge_fullCircle: false, - gauge_label_show: true, - gauge_label_format: undefined, - gauge_min: 0, - gauge_max: 100, - gauge_startingAngle: -1 * Math.PI/2, - gauge_units: undefined, - gauge_width: undefined, - gauge_expand: {}, - gauge_expand_duration: 50, - // donut - donut_label_show: true, - donut_label_format: undefined, - donut_label_threshold: 0.05, - donut_label_ratio: undefined, - donut_width: undefined, - donut_title: "", - donut_expand: {}, - donut_expand_duration: 50, - // spline - spline_interpolation_type: 'cardinal', - // region - region to change style - regions: [], - // tooltip - show when mouseover on each data - tooltip_show: true, - tooltip_grouped: true, - tooltip_format_title: undefined, - tooltip_format_name: undefined, - tooltip_format_value: undefined, - tooltip_position: undefined, - tooltip_contents: function (d, defaultTitleFormat, defaultValueFormat, color) { - return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : ''; - }, - tooltip_init_show: false, - tooltip_init_x: 0, - tooltip_init_position: {top: '0px', left: '50px'}, - tooltip_onshow: function () {}, - tooltip_onhide: function () {}, - // title - title_text: undefined, - title_padding: { - top: 0, - right: 0, - bottom: 0, - left: 0 - }, - title_position: 'top-center', - }; - - Object.keys(this.additionalConfig).forEach(function (key) { - config[key] = this.additionalConfig[key]; - }, this); - - return config; - }; - c3_chart_internal_fn.additionalConfig = {}; - - c3_chart_internal_fn.loadConfig = function (config) { - var this_config = this.config, target, keys, read; - function find() { - var key = keys.shift(); - // console.log("key =>", key, ", target =>", target); - if (key && target && typeof target === 'object' && key in target) { - target = target[key]; - return find(); - } - else if (!key) { - return target; - } - else { - return undefined; - } - } - Object.keys(this_config).forEach(function (key) { - target = config; - keys = key.split('_'); - read = find(); - // console.log("CONFIG : ", key, read); - if (isDefined(read)) { - this_config[key] = read; - } - }); - }; - - c3_chart_internal_fn.getScale = function (min, max, forTimeseries) { - return (forTimeseries ? this.d3.time.scale() : this.d3.scale.linear()).range([min, max]); - }; - c3_chart_internal_fn.getX = function (min, max, domain, offset) { - var $$ = this, - scale = $$.getScale(min, max, $$.isTimeSeries()), - _scale = domain ? scale.domain(domain) : scale, key; - // Define customized scale if categorized axis - if ($$.isCategorized()) { - offset = offset || function () { return 0; }; - scale = function (d, raw) { - var v = _scale(d) + offset(d); - return raw ? v : Math.ceil(v); - }; - } else { - scale = function (d, raw) { - var v = _scale(d); - return raw ? v : Math.ceil(v); - }; - } - // define functions - for (key in _scale) { - scale[key] = _scale[key]; - } - scale.orgDomain = function () { - return _scale.domain(); - }; - // define custom domain() for categorized axis - if ($$.isCategorized()) { - scale.domain = function (domain) { - if (!arguments.length) { - domain = this.orgDomain(); - return [domain[0], domain[1] + 1]; - } - _scale.domain(domain); - return scale; - }; - } - return scale; - }; - c3_chart_internal_fn.getY = function (min, max, domain) { - var scale = this.getScale(min, max, this.isTimeSeriesY()); - if (domain) { scale.domain(domain); } - return scale; - }; - c3_chart_internal_fn.getYScale = function (id) { - return this.axis.getId(id) === 'y2' ? this.y2 : this.y; - }; - c3_chart_internal_fn.getSubYScale = function (id) { - return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY; - }; - c3_chart_internal_fn.updateScales = function () { - var $$ = this, config = $$.config, - forInit = !$$.x; - // update edges - $$.xMin = config.axis_rotated ? 1 : 0; - $$.xMax = config.axis_rotated ? $$.height : $$.width; - $$.yMin = config.axis_rotated ? 0 : $$.height; - $$.yMax = config.axis_rotated ? $$.width : 1; - $$.subXMin = $$.xMin; - $$.subXMax = $$.xMax; - $$.subYMin = config.axis_rotated ? 0 : $$.height2; - $$.subYMax = config.axis_rotated ? $$.width2 : 1; - // update scales - $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () { return $$.xAxis.tickOffset(); }); - $$.y = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y_default : $$.y.domain()); - $$.y2 = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y2_default : $$.y2.domain()); - $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function (d) { return d % 1 ? 0 : $$.subXAxis.tickOffset(); }); - $$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y_default : $$.subY.domain()); - $$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain()); - // update axes - $$.xAxisTickFormat = $$.axis.getXAxisTickFormat(); - $$.xAxisTickValues = $$.axis.getXAxisTickValues(); - $$.yAxisTickValues = $$.axis.getYAxisTickValues(); - $$.y2AxisTickValues = $$.axis.getY2AxisTickValues(); - - $$.xAxis = $$.axis.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); - $$.subXAxis = $$.axis.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); - $$.yAxis = $$.axis.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, config.axis_y_tick_outer); - $$.y2Axis = $$.axis.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, config.axis_y2_tick_outer); - - // Set initialized scales to brush and zoom - if (!forInit) { - if ($$.brush) { $$.brush.scale($$.subX); } - if (config.zoom_enabled) { $$.zoom.scale($$.x); } - } - // update for arc - if ($$.updateArc) { $$.updateArc(); } - }; - - c3_chart_internal_fn.getYDomainMin = function (targets) { - var $$ = this, config = $$.config, - ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), - j, k, baseId, idsInGroup, id, hasNegativeValue; - if (config.data_groups.length > 0) { - hasNegativeValue = $$.hasNegativeValueInTargets(targets); - for (j = 0; j < config.data_groups.length; j++) { - // Determine baseId - idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; }); - if (idsInGroup.length === 0) { continue; } - baseId = idsInGroup[0]; - // Consider negative values - if (hasNegativeValue && ys[baseId]) { - ys[baseId].forEach(function (v, i) { - ys[baseId][i] = v < 0 ? v : 0; - }); - } - // Compute min - for (k = 1; k < idsInGroup.length; k++) { - id = idsInGroup[k]; - if (! ys[id]) { continue; } - ys[id].forEach(function (v, i) { - if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) { - ys[baseId][i] += +v; - } - }); - } - } - } - return $$.d3.min(Object.keys(ys).map(function (key) { return $$.d3.min(ys[key]); })); - }; - c3_chart_internal_fn.getYDomainMax = function (targets) { - var $$ = this, config = $$.config, - ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), - j, k, baseId, idsInGroup, id, hasPositiveValue; - if (config.data_groups.length > 0) { - hasPositiveValue = $$.hasPositiveValueInTargets(targets); - for (j = 0; j < config.data_groups.length; j++) { - // Determine baseId - idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; }); - if (idsInGroup.length === 0) { continue; } - baseId = idsInGroup[0]; - // Consider positive values - if (hasPositiveValue && ys[baseId]) { - ys[baseId].forEach(function (v, i) { - ys[baseId][i] = v > 0 ? v : 0; - }); - } - // Compute max - for (k = 1; k < idsInGroup.length; k++) { - id = idsInGroup[k]; - if (! ys[id]) { continue; } - ys[id].forEach(function (v, i) { - if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) { - ys[baseId][i] += +v; - } - }); - } - } - } - return $$.d3.max(Object.keys(ys).map(function (key) { return $$.d3.max(ys[key]); })); - }; - c3_chart_internal_fn.getYDomain = function (targets, axisId, xDomain) { - var $$ = this, config = $$.config, - targetsByAxisId = targets.filter(function (t) { return $$.axis.getId(t.id) === axisId; }), - yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId, - yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min, - yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max, - yDomainMin = $$.getYDomainMin(yTargets), - yDomainMax = $$.getYDomainMax(yTargets), - domain, domainLength, padding, padding_top, padding_bottom, - center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center, - yDomainAbs, lengths, diff, ratio, isAllPositive, isAllNegative, - isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased), - isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted, - showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated, - showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated; - - // MEMO: avoid inverting domain unexpectedly - yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? (yDomainMin < yMax ? yDomainMin : yMax - 10) : yDomainMin; - yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? (yMin < yDomainMax ? yDomainMax : yMin + 10) : yDomainMax; - - if (yTargets.length === 0) { // use current domain if target of axisId is none - return axisId === 'y2' ? $$.y2.domain() : $$.y.domain(); - } - if (isNaN(yDomainMin)) { // set minimum to zero when not number - yDomainMin = 0; - } - if (isNaN(yDomainMax)) { // set maximum to have same value as yDomainMin - yDomainMax = yDomainMin; - } - if (yDomainMin === yDomainMax) { - yDomainMin < 0 ? yDomainMax = 0 : yDomainMin = 0; - } - isAllPositive = yDomainMin >= 0 && yDomainMax >= 0; - isAllNegative = yDomainMin <= 0 && yDomainMax <= 0; - - // Cancel zerobased if axis_*_min / axis_*_max specified - if ((isValue(yMin) && isAllPositive) || (isValue(yMax) && isAllNegative)) { - isZeroBased = false; - } - - // Bar/Area chart should be 0-based if all positive|negative - if (isZeroBased) { - if (isAllPositive) { yDomainMin = 0; } - if (isAllNegative) { yDomainMax = 0; } - } - - domainLength = Math.abs(yDomainMax - yDomainMin); - padding = padding_top = padding_bottom = domainLength * 0.1; - - if (typeof center !== 'undefined') { - yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax)); - yDomainMax = center + yDomainAbs; - yDomainMin = center - yDomainAbs; - } - // add padding for data label - if (showHorizontalDataLabel) { - lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width'); - diff = diffDomain($$.y.range()); - ratio = [lengths[0] / diff, lengths[1] / diff]; - padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1])); - padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1])); - } else if (showVerticalDataLabel) { - lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height'); - padding_top += $$.axis.convertPixelsToAxisPadding(lengths[1], domainLength); - padding_bottom += $$.axis.convertPixelsToAxisPadding(lengths[0], domainLength); - } - if (axisId === 'y' && notEmpty(config.axis_y_padding)) { - padding_top = $$.axis.getPadding(config.axis_y_padding, 'top', padding_top, domainLength); - padding_bottom = $$.axis.getPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength); - } - if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) { - padding_top = $$.axis.getPadding(config.axis_y2_padding, 'top', padding_top, domainLength); - padding_bottom = $$.axis.getPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength); - } - // Bar/Area chart should be 0-based if all positive|negative - if (isZeroBased) { - if (isAllPositive) { padding_bottom = yDomainMin; } - if (isAllNegative) { padding_top = -yDomainMax; } - } - domain = [yDomainMin - padding_bottom, yDomainMax + padding_top]; - return isInverted ? domain.reverse() : domain; - }; - c3_chart_internal_fn.getXDomainMin = function (targets) { - var $$ = this, config = $$.config; - return isDefined(config.axis_x_min) ? - ($$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min) : - $$.d3.min(targets, function (t) { return $$.d3.min(t.values, function (v) { return v.x; }); }); - }; - c3_chart_internal_fn.getXDomainMax = function (targets) { - var $$ = this, config = $$.config; - return isDefined(config.axis_x_max) ? - ($$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max) : - $$.d3.max(targets, function (t) { return $$.d3.max(t.values, function (v) { return v.x; }); }); - }; - c3_chart_internal_fn.getXDomainPadding = function (domain) { - var $$ = this, config = $$.config, - diff = domain[1] - domain[0], - maxDataCount, padding, paddingLeft, paddingRight; - if ($$.isCategorized()) { - padding = 0; - } else if ($$.hasType('bar')) { - maxDataCount = $$.getMaxDataCount(); - padding = maxDataCount > 1 ? (diff / (maxDataCount - 1)) / 2 : 0.5; - } else { - padding = diff * 0.01; - } - if (typeof config.axis_x_padding === 'object' && notEmpty(config.axis_x_padding)) { - paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding; - paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding; - } else if (typeof config.axis_x_padding === 'number') { - paddingLeft = paddingRight = config.axis_x_padding; - } else { - paddingLeft = paddingRight = padding; - } - return {left: paddingLeft, right: paddingRight}; - }; - c3_chart_internal_fn.getXDomain = function (targets) { - var $$ = this, - xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)], - firstX = xDomain[0], lastX = xDomain[1], - padding = $$.getXDomainPadding(xDomain), - min = 0, max = 0; - // show center of x domain if min and max are the same - if ((firstX - lastX) === 0 && !$$.isCategorized()) { - if ($$.isTimeSeries()) { - firstX = new Date(firstX.getTime() * 0.5); - lastX = new Date(lastX.getTime() * 1.5); - } else { - firstX = firstX === 0 ? 1 : (firstX * 0.5); - lastX = lastX === 0 ? -1 : (lastX * 1.5); - } - } - if (firstX || firstX === 0) { - min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left; - } - if (lastX || lastX === 0) { - max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right; - } - return [min, max]; - }; - c3_chart_internal_fn.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain) { - var $$ = this, config = $$.config; - - if (withUpdateOrgXDomain) { - $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets))); - $$.orgXDomain = $$.x.domain(); - if (config.zoom_enabled) { $$.zoom.scale($$.x).updateScaleExtent(); } - $$.subX.domain($$.x.domain()); - if ($$.brush) { $$.brush.scale($$.subX); } - } - if (withUpdateXDomain) { - $$.x.domain(domain ? domain : (!$$.brush || $$.brush.empty()) ? $$.orgXDomain : $$.brush.extent()); - if (config.zoom_enabled) { $$.zoom.scale($$.x).updateScaleExtent(); } - } - - // Trim domain when too big by zoom mousemove event - if (withTrim) { $$.x.domain($$.trimXDomain($$.x.orgDomain())); } - - return $$.x.domain(); - }; - c3_chart_internal_fn.trimXDomain = function (domain) { - var zoomDomain = this.getZoomDomain(), - min = zoomDomain[0], max = zoomDomain[1]; - if (domain[0] <= min) { - domain[1] = +domain[1] + (min - domain[0]); - domain[0] = min; - } - if (max <= domain[1]) { - domain[0] = +domain[0] - (domain[1] - max); - domain[1] = max; - } - return domain; - }; - - c3_chart_internal_fn.isX = function (key) { - var $$ = this, config = $$.config; - return (config.data_x && key === config.data_x) || (notEmpty(config.data_xs) && hasValue(config.data_xs, key)); - }; - c3_chart_internal_fn.isNotX = function (key) { - return !this.isX(key); - }; - c3_chart_internal_fn.getXKey = function (id) { - var $$ = this, config = $$.config; - return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null; - }; - c3_chart_internal_fn.getXValuesOfXKey = function (key, targets) { - var $$ = this, - xValues, ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : []; - ids.forEach(function (id) { - if ($$.getXKey(id) === key) { - xValues = $$.data.xs[id]; - } - }); - return xValues; - }; - c3_chart_internal_fn.getIndexByX = function (x) { - var $$ = this, - data = $$.filterByX($$.data.targets, x); - return data.length ? data[0].index : null; - }; - c3_chart_internal_fn.getXValue = function (id, i) { - var $$ = this; - return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i; - }; - c3_chart_internal_fn.getOtherTargetXs = function () { - var $$ = this, - idsForX = Object.keys($$.data.xs); - return idsForX.length ? $$.data.xs[idsForX[0]] : null; - }; - c3_chart_internal_fn.getOtherTargetX = function (index) { - var xs = this.getOtherTargetXs(); - return xs && index < xs.length ? xs[index] : null; - }; - c3_chart_internal_fn.addXs = function (xs) { - var $$ = this; - Object.keys(xs).forEach(function (id) { - $$.config.data_xs[id] = xs[id]; - }); - }; - c3_chart_internal_fn.hasMultipleX = function (xs) { - return this.d3.set(Object.keys(xs).map(function (id) { return xs[id]; })).size() > 1; - }; - c3_chart_internal_fn.isMultipleX = function () { - return notEmpty(this.config.data_xs) || !this.config.data_xSort || this.hasType('scatter'); - }; - c3_chart_internal_fn.addName = function (data) { - var $$ = this, name; - if (data) { - name = $$.config.data_names[data.id]; - data.name = name !== undefined ? name : data.id; - } - return data; - }; - c3_chart_internal_fn.getValueOnIndex = function (values, index) { - var valueOnIndex = values.filter(function (v) { return v.index === index; }); - return valueOnIndex.length ? valueOnIndex[0] : null; - }; - c3_chart_internal_fn.updateTargetX = function (targets, x) { - var $$ = this; - targets.forEach(function (t) { - t.values.forEach(function (v, i) { - v.x = $$.generateTargetX(x[i], t.id, i); - }); - $$.data.xs[t.id] = x; - }); - }; - c3_chart_internal_fn.updateTargetXs = function (targets, xs) { - var $$ = this; - targets.forEach(function (t) { - if (xs[t.id]) { - $$.updateTargetX([t], xs[t.id]); - } - }); - }; - c3_chart_internal_fn.generateTargetX = function (rawX, id, index) { - var $$ = this, x; - if ($$.isTimeSeries()) { - x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index)); - } - else if ($$.isCustomX() && !$$.isCategorized()) { - x = isValue(rawX) ? +rawX : $$.getXValue(id, index); - } - else { - x = index; - } - return x; - }; - c3_chart_internal_fn.cloneTarget = function (target) { - return { - id : target.id, - id_org : target.id_org, - values : target.values.map(function (d) { - return {x: d.x, value: d.value, id: d.id}; - }) - }; - }; - c3_chart_internal_fn.updateXs = function () { - var $$ = this; - if ($$.data.targets.length) { - $$.xs = []; - $$.data.targets[0].values.forEach(function (v) { - $$.xs[v.index] = v.x; - }); - } - }; - c3_chart_internal_fn.getPrevX = function (i) { - var x = this.xs[i - 1]; - return typeof x !== 'undefined' ? x : null; - }; - c3_chart_internal_fn.getNextX = function (i) { - var x = this.xs[i + 1]; - return typeof x !== 'undefined' ? x : null; - }; - c3_chart_internal_fn.getMaxDataCount = function () { - var $$ = this; - return $$.d3.max($$.data.targets, function (t) { return t.values.length; }); - }; - c3_chart_internal_fn.getMaxDataCountTarget = function (targets) { - var length = targets.length, max = 0, maxTarget; - if (length > 1) { - targets.forEach(function (t) { - if (t.values.length > max) { - maxTarget = t; - max = t.values.length; - } - }); - } else { - maxTarget = length ? targets[0] : null; - } - return maxTarget; - }; - c3_chart_internal_fn.getEdgeX = function (targets) { - var $$ = this; - return !targets.length ? [0, 0] : [ - $$.d3.min(targets, function (t) { return t.values[0].x; }), - $$.d3.max(targets, function (t) { return t.values[t.values.length - 1].x; }) - ]; - }; - c3_chart_internal_fn.mapToIds = function (targets) { - return targets.map(function (d) { return d.id; }); - }; - c3_chart_internal_fn.mapToTargetIds = function (ids) { - var $$ = this; - return ids ? [].concat(ids) : $$.mapToIds($$.data.targets); - }; - c3_chart_internal_fn.hasTarget = function (targets, id) { - var ids = this.mapToIds(targets), i; - for (i = 0; i < ids.length; i++) { - if (ids[i] === id) { - return true; - } - } - return false; - }; - c3_chart_internal_fn.isTargetToShow = function (targetId) { - return this.hiddenTargetIds.indexOf(targetId) < 0; - }; - c3_chart_internal_fn.isLegendToShow = function (targetId) { - return this.hiddenLegendIds.indexOf(targetId) < 0; - }; - c3_chart_internal_fn.filterTargetsToShow = function (targets) { - var $$ = this; - return targets.filter(function (t) { return $$.isTargetToShow(t.id); }); - }; - c3_chart_internal_fn.mapTargetsToUniqueXs = function (targets) { - var $$ = this; - var xs = $$.d3.set($$.d3.merge(targets.map(function (t) { return t.values.map(function (v) { return +v.x; }); }))).values(); - xs = $$.isTimeSeries() ? xs.map(function (x) { return new Date(+x); }) : xs.map(function (x) { return +x; }); - return xs.sort(function (a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; }); - }; - c3_chart_internal_fn.addHiddenTargetIds = function (targetIds) { - this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds); - }; - c3_chart_internal_fn.removeHiddenTargetIds = function (targetIds) { - this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); - }; - c3_chart_internal_fn.addHiddenLegendIds = function (targetIds) { - this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds); - }; - c3_chart_internal_fn.removeHiddenLegendIds = function (targetIds) { - this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); - }; - c3_chart_internal_fn.getValuesAsIdKeyed = function (targets) { - var ys = {}; - targets.forEach(function (t) { - ys[t.id] = []; - t.values.forEach(function (v) { - ys[t.id].push(v.value); - }); - }); - return ys; - }; - c3_chart_internal_fn.checkValueInTargets = function (targets, checker) { - var ids = Object.keys(targets), i, j, values; - for (i = 0; i < ids.length; i++) { - values = targets[ids[i]].values; - for (j = 0; j < values.length; j++) { - if (checker(values[j].value)) { - return true; - } - } - } - return false; - }; - c3_chart_internal_fn.hasNegativeValueInTargets = function (targets) { - return this.checkValueInTargets(targets, function (v) { return v < 0; }); - }; - c3_chart_internal_fn.hasPositiveValueInTargets = function (targets) { - return this.checkValueInTargets(targets, function (v) { return v > 0; }); - }; - c3_chart_internal_fn.isOrderDesc = function () { - var config = this.config; - return typeof(config.data_order) === 'string' && config.data_order.toLowerCase() === 'desc'; - }; - c3_chart_internal_fn.isOrderAsc = function () { - var config = this.config; - return typeof(config.data_order) === 'string' && config.data_order.toLowerCase() === 'asc'; - }; - c3_chart_internal_fn.orderTargets = function (targets) { - var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc(); - if (orderAsc || orderDesc) { - targets.sort(function (t1, t2) { - var reducer = function (p, c) { return p + Math.abs(c.value); }; - var t1Sum = t1.values.reduce(reducer, 0), - t2Sum = t2.values.reduce(reducer, 0); - return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum; - }); - } else if (isFunction(config.data_order)) { - targets.sort(config.data_order); - } // TODO: accept name array for order - return targets; - }; - c3_chart_internal_fn.filterByX = function (targets, x) { - return this.d3.merge(targets.map(function (t) { return t.values; })).filter(function (v) { return v.x - x === 0; }); - }; - c3_chart_internal_fn.filterRemoveNull = function (data) { - return data.filter(function (d) { return isValue(d.value); }); - }; - c3_chart_internal_fn.filterByXDomain = function (targets, xDomain) { - return targets.map(function (t) { - return { - id: t.id, - id_org: t.id_org, - values: t.values.filter(function (v) { - return xDomain[0] <= v.x && v.x <= xDomain[1]; - }) - }; - }); - }; - c3_chart_internal_fn.hasDataLabel = function () { - var config = this.config; - if (typeof config.data_labels === 'boolean' && config.data_labels) { - return true; - } else if (typeof config.data_labels === 'object' && notEmpty(config.data_labels)) { - return true; - } - return false; - }; - c3_chart_internal_fn.getDataLabelLength = function (min, max, key) { - var $$ = this, - lengths = [0, 0], paddingCoef = 1.3; - $$.selectChart.select('svg').selectAll('.dummy') - .data([min, max]) - .enter().append('text') - .text(function (d) { return $$.dataLabelFormat(d.id)(d); }) - .each(function (d, i) { - lengths[i] = this.getBoundingClientRect()[key] * paddingCoef; - }) - .remove(); - return lengths; - }; - c3_chart_internal_fn.isNoneArc = function (d) { - return this.hasTarget(this.data.targets, d.id); - }, - c3_chart_internal_fn.isArc = function (d) { - return 'data' in d && this.hasTarget(this.data.targets, d.data.id); - }; - c3_chart_internal_fn.findSameXOfValues = function (values, index) { - var i, targetX = values[index].x, sames = []; - for (i = index - 1; i >= 0; i--) { - if (targetX !== values[i].x) { break; } - sames.push(values[i]); - } - for (i = index; i < values.length; i++) { - if (targetX !== values[i].x) { break; } - sames.push(values[i]); - } - return sames; - }; - - c3_chart_internal_fn.findClosestFromTargets = function (targets, pos) { - var $$ = this, candidates; - - // map to array of closest points of each target - candidates = targets.map(function (target) { - return $$.findClosest(target.values, pos); - }); - - // decide closest point and return - return $$.findClosest(candidates, pos); - }; - c3_chart_internal_fn.findClosest = function (values, pos) { - var $$ = this, minDist = $$.config.point_sensitivity, closest; - - // find mouseovering bar - values.filter(function (v) { return v && $$.isBarType(v.id); }).forEach(function (v) { - var shape = $$.main.select('.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index).node(); - if (!closest && $$.isWithinBar(shape)) { - closest = v; - } - }); - - // find closest point from non-bar - values.filter(function (v) { return v && !$$.isBarType(v.id); }).forEach(function (v) { - var d = $$.dist(v, pos); - if (d < minDist) { - minDist = d; - closest = v; - } - }); - - return closest; - }; - c3_chart_internal_fn.dist = function (data, pos) { - var $$ = this, config = $$.config, - xIndex = config.axis_rotated ? 1 : 0, - yIndex = config.axis_rotated ? 0 : 1, - y = $$.circleY(data, data.index), - x = $$.x(data.x); - return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2)); - }; - c3_chart_internal_fn.convertValuesToStep = function (values) { - var converted = [].concat(values), i; - - if (!this.isCategorized()) { - return values; - } - - for (i = values.length + 1; 0 < i; i--) { - converted[i] = converted[i - 1]; - } - - converted[0] = { - x: converted[0].x - 1, - value: converted[0].value, - id: converted[0].id - }; - converted[values.length + 1] = { - x: converted[values.length].x + 1, - value: converted[values.length].value, - id: converted[values.length].id - }; - - return converted; - }; - c3_chart_internal_fn.updateDataAttributes = function (name, attrs) { - var $$ = this, config = $$.config, current = config['data_' + name]; - if (typeof attrs === 'undefined') { return current; } - Object.keys(attrs).forEach(function (id) { - current[id] = attrs[id]; - }); - $$.redraw({withLegend: true}); - return current; - }; - - c3_chart_internal_fn.convertUrlToData = function (url, mimeType, headers, keys, done) { - var $$ = this, type = mimeType ? mimeType : 'csv'; - var req = $$.d3.xhr(url); - if (headers) { - Object.keys(headers).forEach(function (header) { - req.header(header, headers[header]); - }); - } - req.get(function (error, data) { - var d; - if (!data) { - throw new Error(error.responseURL + ' ' + error.status + ' (' + error.statusText + ')'); - } - if (type === 'json') { - d = $$.convertJsonToData(JSON.parse(data.response), keys); - } else if (type === 'tsv') { - d = $$.convertTsvToData(data.response); - } else { - d = $$.convertCsvToData(data.response); - } - done.call($$, d); - }); - }; - c3_chart_internal_fn.convertXsvToData = function (xsv, parser) { - var rows = parser.parseRows(xsv), d; - if (rows.length === 1) { - d = [{}]; - rows[0].forEach(function (id) { - d[0][id] = null; - }); - } else { - d = parser.parse(xsv); - } - return d; - }; - c3_chart_internal_fn.convertCsvToData = function (csv) { - return this.convertXsvToData(csv, this.d3.csv); - }; - c3_chart_internal_fn.convertTsvToData = function (tsv) { - return this.convertXsvToData(tsv, this.d3.tsv); - }; - c3_chart_internal_fn.convertJsonToData = function (json, keys) { - var $$ = this, - new_rows = [], targetKeys, data; - if (keys) { // when keys specified, json would be an array that includes objects - if (keys.x) { - targetKeys = keys.value.concat(keys.x); - $$.config.data_x = keys.x; - } else { - targetKeys = keys.value; - } - new_rows.push(targetKeys); - json.forEach(function (o) { - var new_row = []; - targetKeys.forEach(function (key) { - // convert undefined to null because undefined data will be removed in convertDataToTargets() - var v = $$.findValueInJson(o, key); - if (isUndefined(v)) { - v = null; - } - new_row.push(v); - }); - new_rows.push(new_row); - }); - data = $$.convertRowsToData(new_rows); - } else { - Object.keys(json).forEach(function (key) { - new_rows.push([key].concat(json[key])); - }); - data = $$.convertColumnsToData(new_rows); - } - return data; - }; - c3_chart_internal_fn.findValueInJson = function (object, path) { - path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties (replace [] with .) - path = path.replace(/^\./, ''); // strip a leading dot - var pathArray = path.split('.'); - for (var i = 0; i < pathArray.length; ++i) { - var k = pathArray[i]; - if (k in object) { - object = object[k]; - } else { - return; - } - } - return object; - }; - c3_chart_internal_fn.convertRowsToData = function (rows) { - var keys = rows[0], new_row = {}, new_rows = [], i, j; - for (i = 1; i < rows.length; i++) { - new_row = {}; - for (j = 0; j < rows[i].length; j++) { - if (isUndefined(rows[i][j])) { - throw new Error("Source data is missing a component at (" + i + "," + j + ")!"); - } - new_row[keys[j]] = rows[i][j]; - } - new_rows.push(new_row); - } - return new_rows; - }; - c3_chart_internal_fn.convertColumnsToData = function (columns) { - var new_rows = [], i, j, key; - for (i = 0; i < columns.length; i++) { - key = columns[i][0]; - for (j = 1; j < columns[i].length; j++) { - if (isUndefined(new_rows[j - 1])) { - new_rows[j - 1] = {}; - } - if (isUndefined(columns[i][j])) { - throw new Error("Source data is missing a component at (" + i + "," + j + ")!"); - } - new_rows[j - 1][key] = columns[i][j]; - } - } - return new_rows; - }; - c3_chart_internal_fn.convertDataToTargets = function (data, appendXs) { - var $$ = this, config = $$.config, - ids = $$.d3.keys(data[0]).filter($$.isNotX, $$), - xs = $$.d3.keys(data[0]).filter($$.isX, $$), - targets; - - // save x for update data by load when custom x and c3.x API - ids.forEach(function (id) { - var xKey = $$.getXKey(id); - - if ($$.isCustomX() || $$.isTimeSeries()) { - // if included in input data - if (xs.indexOf(xKey) >= 0) { - $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : []).concat( - data.map(function (d) { return d[xKey]; }) - .filter(isValue) - .map(function (rawX, i) { return $$.generateTargetX(rawX, id, i); }) - ); - } - // if not included in input data, find from preloaded data of other id's x - else if (config.data_x) { - $$.data.xs[id] = $$.getOtherTargetXs(); - } - // if not included in input data, find from preloaded data - else if (notEmpty(config.data_xs)) { - $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets); - } - // MEMO: if no x included, use same x of current will be used - } else { - $$.data.xs[id] = data.map(function (d, i) { return i; }); - } - }); - - - // check x is defined - ids.forEach(function (id) { - if (!$$.data.xs[id]) { - throw new Error('x is not defined for id = "' + id + '".'); - } - }); - - // convert to target - targets = ids.map(function (id, index) { - var convertedId = config.data_idConverter(id); - return { - id: convertedId, - id_org: id, - values: data.map(function (d, i) { - var xKey = $$.getXKey(id), rawX = d[xKey], - value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null, x; - // use x as categories if custom x and categorized - if ($$.isCustomX() && $$.isCategorized() && index === 0 && !isUndefined(rawX)) { - if (index === 0 && i === 0) { - config.axis_x_categories = []; - } - x = config.axis_x_categories.indexOf(rawX); - if (x === -1) { - x = config.axis_x_categories.length; - config.axis_x_categories.push(rawX); - } - } else { - x = $$.generateTargetX(rawX, id, i); - } - // mark as x = undefined if value is undefined and filter to remove after mapped - if (isUndefined(d[id]) || $$.data.xs[id].length <= i) { - x = undefined; - } - return {x: x, value: value, id: convertedId}; - }).filter(function (v) { return isDefined(v.x); }) - }; - }); - - // finish targets - targets.forEach(function (t) { - var i; - // sort values by its x - if (config.data_xSort) { - t.values = t.values.sort(function (v1, v2) { - var x1 = v1.x || v1.x === 0 ? v1.x : Infinity, - x2 = v2.x || v2.x === 0 ? v2.x : Infinity; - return x1 - x2; - }); - } - // indexing each value - i = 0; - t.values.forEach(function (v) { - v.index = i++; - }); - // this needs to be sorted because its index and value.index is identical - $$.data.xs[t.id].sort(function (v1, v2) { - return v1 - v2; - }); - }); - - // cache information about values - $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets); - $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets); - - // set target types - if (config.data_type) { - $$.setTargetType($$.mapToIds(targets).filter(function (id) { return ! (id in config.data_types); }), config.data_type); - } - - // cache as original id keyed - targets.forEach(function (d) { - $$.addCache(d.id_org, d); - }); - - return targets; - }; - - c3_chart_internal_fn.load = function (targets, args) { - var $$ = this; - if (targets) { - // filter loading targets if needed - if (args.filter) { - targets = targets.filter(args.filter); - } - // set type if args.types || args.type specified - if (args.type || args.types) { - targets.forEach(function (t) { - var type = args.types && args.types[t.id] ? args.types[t.id] : args.type; - $$.setTargetType(t.id, type); - }); - } - // Update/Add data - $$.data.targets.forEach(function (d) { - for (var i = 0; i < targets.length; i++) { - if (d.id === targets[i].id) { - d.values = targets[i].values; - targets.splice(i, 1); - break; - } - } - }); - $$.data.targets = $$.data.targets.concat(targets); // add remained - } - - // Set targets - $$.updateTargets($$.data.targets); - - // Redraw with new targets - $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true}); - - if (args.done) { args.done(); } - }; - c3_chart_internal_fn.loadFromArgs = function (args) { - var $$ = this; - if (args.data) { - $$.load($$.convertDataToTargets(args.data), args); - } - else if (args.url) { - $$.convertUrlToData(args.url, args.mimeType, args.headers, args.keys, function (data) { - $$.load($$.convertDataToTargets(data), args); - }); - } - else if (args.json) { - $$.load($$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args); - } - else if (args.rows) { - $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args); - } - else if (args.columns) { - $$.load($$.convertDataToTargets($$.convertColumnsToData(args.columns)), args); - } - else { - $$.load(null, args); - } - }; - c3_chart_internal_fn.unload = function (targetIds, done) { - var $$ = this; - if (!done) { - done = function () {}; - } - // filter existing target - targetIds = targetIds.filter(function (id) { return $$.hasTarget($$.data.targets, id); }); - // If no target, call done and return - if (!targetIds || targetIds.length === 0) { - done(); - return; - } - $$.svg.selectAll(targetIds.map(function (id) { return $$.selectorTarget(id); })) - .transition() - .style('opacity', 0) - .remove() - .call($$.endall, done); - targetIds.forEach(function (id) { - // Reset fadein for future load - $$.withoutFadeIn[id] = false; - // Remove target's elements - if ($$.legend) { - $$.legend.selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)).remove(); - } - // Remove target - $$.data.targets = $$.data.targets.filter(function (t) { - return t.id !== id; - }); - }); - }; - - c3_chart_internal_fn.categoryName = function (i) { - var config = this.config; - return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i; - }; - - c3_chart_internal_fn.initEventRect = function () { - var $$ = this; - $$.main.select('.' + CLASS.chart).append("g") - .attr("class", CLASS.eventRects) - .style('fill-opacity', 0); - }; - c3_chart_internal_fn.redrawEventRect = function () { - var $$ = this, config = $$.config, - eventRectUpdate, maxDataCountTarget, - isMultipleX = $$.isMultipleX(); - - // rects for mouseover - var eventRects = $$.main.select('.' + CLASS.eventRects) - .style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null) - .classed(CLASS.eventRectsMultiple, isMultipleX) - .classed(CLASS.eventRectsSingle, !isMultipleX); - - // clear old rects - eventRects.selectAll('.' + CLASS.eventRect).remove(); - - // open as public variable - $$.eventRect = eventRects.selectAll('.' + CLASS.eventRect); - - if (isMultipleX) { - eventRectUpdate = $$.eventRect.data([0]); - // enter : only one rect will be added - $$.generateEventRectsForMultipleXs(eventRectUpdate.enter()); - // update - $$.updateEventRect(eventRectUpdate); - // exit : not needed because always only one rect exists - } - else { - // Set data and update $$.eventRect - maxDataCountTarget = $$.getMaxDataCountTarget($$.data.targets); - eventRects.datum(maxDataCountTarget ? maxDataCountTarget.values : []); - $$.eventRect = eventRects.selectAll('.' + CLASS.eventRect); - eventRectUpdate = $$.eventRect.data(function (d) { return d; }); - // enter - $$.generateEventRectsForSingleX(eventRectUpdate.enter()); - // update - $$.updateEventRect(eventRectUpdate); - // exit - eventRectUpdate.exit().remove(); - } - }; - c3_chart_internal_fn.updateEventRect = function (eventRectUpdate) { - var $$ = this, config = $$.config, - x, y, w, h, rectW, rectX; - - // set update selection if null - eventRectUpdate = eventRectUpdate || $$.eventRect.data(function (d) { return d; }); - - if ($$.isMultipleX()) { - // TODO: rotated not supported yet - x = 0; - y = 0; - w = $$.width; - h = $$.height; - } - else { - if (($$.isCustomX() || $$.isTimeSeries()) && !$$.isCategorized()) { - - // update index for x that is used by prevX and nextX - $$.updateXs(); - - rectW = function (d) { - var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index); - - // if there this is a single data point make the eventRect full width (or height) - if (prevX === null && nextX === null) { - return config.axis_rotated ? $$.height : $$.width; - } - - if (prevX === null) { prevX = $$.x.domain()[0]; } - if (nextX === null) { nextX = $$.x.domain()[1]; } - - return Math.max(0, ($$.x(nextX) - $$.x(prevX)) / 2); - }; - rectX = function (d) { - var prevX = $$.getPrevX(d.index), nextX = $$.getNextX(d.index), - thisX = $$.data.xs[d.id][d.index]; - - // if there this is a single data point position the eventRect at 0 - if (prevX === null && nextX === null) { - return 0; - } - - if (prevX === null) { prevX = $$.x.domain()[0]; } - - return ($$.x(thisX) + $$.x(prevX)) / 2; - }; - } else { - rectW = $$.getEventRectWidth(); - rectX = function (d) { - return $$.x(d.x) - (rectW / 2); - }; - } - x = config.axis_rotated ? 0 : rectX; - y = config.axis_rotated ? rectX : 0; - w = config.axis_rotated ? $$.width : rectW; - h = config.axis_rotated ? rectW : $$.height; - } - - eventRectUpdate - .attr('class', $$.classEvent.bind($$)) - .attr("x", x) - .attr("y", y) - .attr("width", w) - .attr("height", h); - }; - c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) { - var $$ = this, d3 = $$.d3, config = $$.config; - eventRectEnter.append("rect") - .attr("class", $$.classEvent.bind($$)) - .style("cursor", config.data_selection_enabled && config.data_selection_grouped ? "pointer" : null) - .on('mouseover', function (d) { - var index = d.index; - - if ($$.dragging || $$.flowing) { return; } // do nothing while dragging/flowing - if ($$.hasArcType()) { return; } - - // Expand shapes for selection - if (config.point_focus_expand_enabled) { $$.expandCircles(index, null, true); } - $$.expandBars(index, null, true); - - // Call event handler - $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) { - config.data_onmouseover.call($$.api, d); - }); - }) - .on('mouseout', function (d) { - var index = d.index; - if (!$$.config) { return; } // chart is destroyed - if ($$.hasArcType()) { return; } - $$.hideXGridFocus(); - $$.hideTooltip(); - // Undo expanded shapes - $$.unexpandCircles(); - $$.unexpandBars(); - // Call event handler - $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) { - config.data_onmouseout.call($$.api, d); - }); - }) - .on('mousemove', function (d) { - var selectedData, index = d.index, - eventRect = $$.svg.select('.' + CLASS.eventRect + '-' + index); - - if ($$.dragging || $$.flowing) { return; } // do nothing while dragging/flowing - if ($$.hasArcType()) { return; } - - if ($$.isStepType(d) && $$.config.line_step_type === 'step-after' && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) { - index -= 1; - } - - // Show tooltip - selectedData = $$.filterTargetsToShow($$.data.targets).map(function (t) { - return $$.addName($$.getValueOnIndex(t.values, index)); - }); - - if (config.tooltip_grouped) { - $$.showTooltip(selectedData, this); - $$.showXGridFocus(selectedData); - } - - if (config.tooltip_grouped && (!config.data_selection_enabled || config.data_selection_grouped)) { - return; - } - - $$.main.selectAll('.' + CLASS.shape + '-' + index) - .each(function () { - d3.select(this).classed(CLASS.EXPANDED, true); - if (config.data_selection_enabled) { - eventRect.style('cursor', config.data_selection_grouped ? 'pointer' : null); - } - if (!config.tooltip_grouped) { - $$.hideXGridFocus(); - $$.hideTooltip(); - if (!config.data_selection_grouped) { - $$.unexpandCircles(index); - $$.unexpandBars(index); - } - } - }) - .filter(function (d) { - return $$.isWithinShape(this, d); - }) - .each(function (d) { - if (config.data_selection_enabled && (config.data_selection_grouped || config.data_selection_isselectable(d))) { - eventRect.style('cursor', 'pointer'); - } - if (!config.tooltip_grouped) { - $$.showTooltip([d], this); - $$.showXGridFocus([d]); - if (config.point_focus_expand_enabled) { $$.expandCircles(index, d.id, true); } - $$.expandBars(index, d.id, true); - } - }); - }) - .on('click', function (d) { - var index = d.index; - if ($$.hasArcType() || !$$.toggleShape) { return; } - if ($$.cancelClick) { - $$.cancelClick = false; - return; - } - if ($$.isStepType(d) && config.line_step_type === 'step-after' && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) { - index -= 1; - } - $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) { - if (config.data_selection_grouped || $$.isWithinShape(this, d)) { - $$.toggleShape(this, d, index); - $$.config.data_onclick.call($$.api, d, this); - } - }); - }) - .call( - config.data_selection_draggable && $$.drag ? ( - d3.behavior.drag().origin(Object) - .on('drag', function () { $$.drag(d3.mouse(this)); }) - .on('dragstart', function () { $$.dragstart(d3.mouse(this)); }) - .on('dragend', function () { $$.dragend(); }) - ) : function () {} - ); - }; - - c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter) { - var $$ = this, d3 = $$.d3, config = $$.config; - - function mouseout() { - $$.svg.select('.' + CLASS.eventRect).style('cursor', null); - $$.hideXGridFocus(); - $$.hideTooltip(); - $$.unexpandCircles(); - $$.unexpandBars(); - } - - eventRectEnter.append('rect') - .attr('x', 0) - .attr('y', 0) - .attr('width', $$.width) - .attr('height', $$.height) - .attr('class', CLASS.eventRect) - .on('mouseout', function () { - if (!$$.config) { return; } // chart is destroyed - if ($$.hasArcType()) { return; } - mouseout(); - }) - .on('mousemove', function () { - var targetsToShow = $$.filterTargetsToShow($$.data.targets); - var mouse, closest, sameXData, selectedData; - - if ($$.dragging) { return; } // do nothing when dragging - if ($$.hasArcType(targetsToShow)) { return; } - - mouse = d3.mouse(this); - closest = $$.findClosestFromTargets(targetsToShow, mouse); - - if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id)) { - config.data_onmouseout.call($$.api, $$.mouseover); - $$.mouseover = undefined; - } - - if (! closest) { - mouseout(); - return; - } - - if ($$.isScatterType(closest) || !config.tooltip_grouped) { - sameXData = [closest]; - } else { - sameXData = $$.filterByX(targetsToShow, closest.x); - } - - // show tooltip when cursor is close to some point - selectedData = sameXData.map(function (d) { - return $$.addName(d); - }); - $$.showTooltip(selectedData, this); - - // expand points - if (config.point_focus_expand_enabled) { - $$.expandCircles(closest.index, closest.id, true); - } - $$.expandBars(closest.index, closest.id, true); - - // Show xgrid focus line - $$.showXGridFocus(selectedData); - - // Show cursor as pointer if point is close to mouse position - if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) { - $$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer'); - if (!$$.mouseover) { - config.data_onmouseover.call($$.api, closest); - $$.mouseover = closest; - } - } - }) - .on('click', function () { - var targetsToShow = $$.filterTargetsToShow($$.data.targets); - var mouse, closest; - if ($$.hasArcType(targetsToShow)) { return; } - - mouse = d3.mouse(this); - closest = $$.findClosestFromTargets(targetsToShow, mouse); - if (! closest) { return; } - // select if selection enabled - if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) { - $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id)).selectAll('.' + CLASS.shape + '-' + closest.index).each(function () { - if (config.data_selection_grouped || $$.isWithinShape(this, closest)) { - $$.toggleShape(this, closest, closest.index); - $$.config.data_onclick.call($$.api, closest, this); - } - }); - } - }) - .call( - config.data_selection_draggable && $$.drag ? ( - d3.behavior.drag().origin(Object) - .on('drag', function () { $$.drag(d3.mouse(this)); }) - .on('dragstart', function () { $$.dragstart(d3.mouse(this)); }) - .on('dragend', function () { $$.dragend(); }) - ) : function () {} - ); - }; - c3_chart_internal_fn.dispatchEvent = function (type, index, mouse) { - var $$ = this, - selector = '.' + CLASS.eventRect + (!$$.isMultipleX() ? '-' + index : ''), - eventRect = $$.main.select(selector).node(), - box = eventRect.getBoundingClientRect(), - x = box.left + (mouse ? mouse[0] : 0), - y = box.top + (mouse ? mouse[1] : 0), - event = document.createEvent("MouseEvents"); - - event.initMouseEvent(type, true, true, window, 0, x, y, x, y, - false, false, false, false, 0, null); - eventRect.dispatchEvent(event); - }; - - c3_chart_internal_fn.getCurrentWidth = function () { - var $$ = this, config = $$.config; - return config.size_width ? config.size_width : $$.getParentWidth(); - }; - c3_chart_internal_fn.getCurrentHeight = function () { - var $$ = this, config = $$.config, - h = config.size_height ? config.size_height : $$.getParentHeight(); - return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1); - }; - c3_chart_internal_fn.getCurrentPaddingTop = function () { - var $$ = this, - config = $$.config, - padding = isValue(config.padding_top) ? config.padding_top : 0; - if ($$.title && $$.title.node()) { - padding += $$.getTitlePadding(); - } - return padding; - }; - c3_chart_internal_fn.getCurrentPaddingBottom = function () { - var config = this.config; - return isValue(config.padding_bottom) ? config.padding_bottom : 0; - }; - c3_chart_internal_fn.getCurrentPaddingLeft = function (withoutRecompute) { - var $$ = this, config = $$.config; - if (isValue(config.padding_left)) { - return config.padding_left; - } else if (config.axis_rotated) { - return !config.axis_x_show ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40); - } else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated - return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1; - } else { - return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute)); - } - }; - c3_chart_internal_fn.getCurrentPaddingRight = function () { - var $$ = this, config = $$.config, - defaultPadding = 10, legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0; - if (isValue(config.padding_right)) { - return config.padding_right + 1; // 1 is needed not to hide tick line - } else if (config.axis_rotated) { - return defaultPadding + legendWidthOnRight; - } else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated - return 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0); - } else { - return ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight; - } - }; - - c3_chart_internal_fn.getParentRectValue = function (key) { - var parent = this.selectChart.node(), v; - while (parent && parent.tagName !== 'BODY') { - try { - v = parent.getBoundingClientRect()[key]; - } catch(e) { - if (key === 'width') { - // In IE in certain cases getBoundingClientRect - // will cause an "unspecified error" - v = parent.offsetWidth; - } - } - if (v) { - break; - } - parent = parent.parentNode; - } - return v; - }; - c3_chart_internal_fn.getParentWidth = function () { - return this.getParentRectValue('width'); - }; - c3_chart_internal_fn.getParentHeight = function () { - var h = this.selectChart.style('height'); - return h.indexOf('px') > 0 ? +h.replace('px', '') : 0; - }; - - - c3_chart_internal_fn.getSvgLeft = function (withoutRecompute) { - var $$ = this, config = $$.config, - hasLeftAxisRect = config.axis_rotated || (!config.axis_rotated && !config.axis_y_inner), - leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY, - leftAxis = $$.main.select('.' + leftAxisClass).node(), - svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : {right: 0}, - chartRect = $$.selectChart.node().getBoundingClientRect(), - hasArc = $$.hasArcType(), - svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute)); - return svgLeft > 0 ? svgLeft : 0; - }; - - - c3_chart_internal_fn.getAxisWidthByAxisId = function (id, withoutRecompute) { - var $$ = this, position = $$.axis.getLabelPositionById(id); - return $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40); - }; - c3_chart_internal_fn.getHorizontalAxisHeight = function (axisId) { - var $$ = this, config = $$.config, h = 30; - if (axisId === 'x' && !config.axis_x_show) { return 8; } - if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height; } - if (axisId === 'y' && !config.axis_y_show) { - return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1; - } - if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top; } - // Calculate x axis height when tick rotated - if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) { - h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - config.axis_x_tick_rotate) / 180); - } - // Calculate y axis height when tick rotated - if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) { - h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - config.axis_y_tick_rotate) / 180); - } - return h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0); - }; - - c3_chart_internal_fn.getEventRectWidth = function () { - return Math.max(0, this.xAxis.tickInterval()); - }; - - c3_chart_internal_fn.getShapeIndices = function (typeFilter) { - var $$ = this, config = $$.config, - indices = {}, i = 0, j, k; - $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach(function (d) { - for (j = 0; j < config.data_groups.length; j++) { - if (config.data_groups[j].indexOf(d.id) < 0) { continue; } - for (k = 0; k < config.data_groups[j].length; k++) { - if (config.data_groups[j][k] in indices) { - indices[d.id] = indices[config.data_groups[j][k]]; - break; - } - } - } - if (isUndefined(indices[d.id])) { indices[d.id] = i++; } - }); - indices.__max__ = i - 1; - return indices; - }; - c3_chart_internal_fn.getShapeX = function (offset, targetsNum, indices, isSub) { - var $$ = this, scale = isSub ? $$.subX : $$.x; - return function (d) { - var index = d.id in indices ? indices[d.id] : 0; - return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0; - }; - }; - c3_chart_internal_fn.getShapeY = function (isSub) { - var $$ = this; - return function (d) { - var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id); - return scale(d.value); - }; - }; - c3_chart_internal_fn.getShapeOffset = function (typeFilter, indices, isSub) { - var $$ = this, - targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))), - targetIds = targets.map(function (t) { return t.id; }); - return function (d, i) { - var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id), - y0 = scale(0), offset = y0; - targets.forEach(function (t) { - var values = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values; - if (t.id === d.id || indices[t.id] !== indices[d.id]) { return; } - if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) { - // check if the x values line up - if (typeof values[i] === 'undefined' || +values[i].x !== +d.x) { // "+" for timeseries - // if not, try to find the value that does line up - i = -1; - values.forEach(function (v, j) { - if (v.x === d.x) { - i = j; - } - }); - } - if (i in values && values[i].value * d.value >= 0) { - offset += scale(values[i].value) - y0; - } - } - }); - return offset; - }; - }; - c3_chart_internal_fn.isWithinShape = function (that, d) { - var $$ = this, - shape = $$.d3.select(that), isWithin; - if (!$$.isTargetToShow(d.id)) { - isWithin = false; - } - else if (that.nodeName === 'circle') { - isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5); - } - else if (that.nodeName === 'path') { - isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar(that) : true; - } - return isWithin; - }; - - - c3_chart_internal_fn.getInterpolate = function (d) { - var $$ = this, - interpolation = $$.isInterpolationType($$.config.spline_interpolation_type) ? $$.config.spline_interpolation_type : 'cardinal'; - return $$.isSplineType(d) ? interpolation : $$.isStepType(d) ? $$.config.line_step_type : "linear"; - }; - - c3_chart_internal_fn.initLine = function () { - var $$ = this; - $$.main.select('.' + CLASS.chart).append("g") - .attr("class", CLASS.chartLines); - }; - c3_chart_internal_fn.updateTargetsForLine = function (targets) { - var $$ = this, config = $$.config, - mainLineUpdate, mainLineEnter, - classChartLine = $$.classChartLine.bind($$), - classLines = $$.classLines.bind($$), - classAreas = $$.classAreas.bind($$), - classCircles = $$.classCircles.bind($$), - classFocus = $$.classFocus.bind($$); - mainLineUpdate = $$.main.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine) - .data(targets) - .attr('class', function (d) { return classChartLine(d) + classFocus(d); }); - mainLineEnter = mainLineUpdate.enter().append('g') - .attr('class', classChartLine) - .style('opacity', 0) - .style("pointer-events", "none"); - // Lines for each data - mainLineEnter.append('g') - .attr("class", classLines); - // Areas - mainLineEnter.append('g') - .attr('class', classAreas); - // Circles for each data point on lines - mainLineEnter.append('g') - .attr("class", function (d) { return $$.generateClass(CLASS.selectedCircles, d.id); }); - mainLineEnter.append('g') - .attr("class", classCircles) - .style("cursor", function (d) { return config.data_selection_isselectable(d) ? "pointer" : null; }); - // Update date for selected circles - targets.forEach(function (t) { - $$.main.selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)).selectAll('.' + CLASS.selectedCircle).each(function (d) { - d.value = t.values[d.index].value; - }); - }); - // MEMO: can not keep same color... - //mainLineUpdate.exit().remove(); - }; - c3_chart_internal_fn.updateLine = function (durationForExit) { - var $$ = this; - $$.mainLine = $$.main.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line) - .data($$.lineData.bind($$)); - $$.mainLine.enter().append('path') - .attr('class', $$.classLine.bind($$)) - .style("stroke", $$.color); - $$.mainLine - .style("opacity", $$.initialOpacity.bind($$)) - .style('shape-rendering', function (d) { return $$.isStepType(d) ? 'crispEdges' : ''; }) - .attr('transform', null); - $$.mainLine.exit().transition().duration(durationForExit) - .style('opacity', 0) - .remove(); - }; - c3_chart_internal_fn.redrawLine = function (drawLine, withTransition) { - return [ - (withTransition ? this.mainLine.transition(Math.random().toString()) : this.mainLine) - .attr("d", drawLine) - .style("stroke", this.color) - .style("opacity", 1) - ]; - }; - c3_chart_internal_fn.generateDrawLine = function (lineIndices, isSub) { - var $$ = this, config = $$.config, - line = $$.d3.svg.line(), - getPoints = $$.generateGetLinePoints(lineIndices, isSub), - yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, - xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); }, - yValue = function (d, i) { - return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value); - }; - - line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue); - if (!config.line_connectNull) { line = line.defined(function (d) { return d.value != null; }); } - return function (d) { - var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, - x = isSub ? $$.x : $$.subX, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path; - if ($$.isLineType(d)) { - if (config.data_regions[d.id]) { - path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]); - } else { - if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } - path = line.interpolate($$.getInterpolate(d))(values); - } - } else { - if (values[0]) { - x0 = x(values[0].x); - y0 = y(values[0].value); - } - path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0; - } - return path ? path : "M 0 0"; - }; - }; - c3_chart_internal_fn.generateGetLinePoints = function (lineIndices, isSub) { // partial duplication of generateGetBarPoints - var $$ = this, config = $$.config, - lineTargetsNum = lineIndices.__max__ + 1, - x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub), - y = $$.getShapeY(!!isSub), - lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub), - yScale = isSub ? $$.getSubYScale : $$.getYScale; - return function (d, i) { - var y0 = yScale.call($$, d.id)(0), - offset = lineOffset(d, i) || y0, // offset is for stacked area chart - posX = x(d), posY = y(d); - // fix posY not to overflow opposite quadrant - if (config.axis_rotated) { - if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } - } - // 1 point that marks the line position - return [ - [posX, posY - (y0 - offset)], - [posX, posY - (y0 - offset)], // needed for compatibility - [posX, posY - (y0 - offset)], // needed for compatibility - [posX, posY - (y0 - offset)] // needed for compatibility - ]; - }; - }; - - - c3_chart_internal_fn.lineWithRegions = function (d, x, y, _regions) { - var $$ = this, config = $$.config, - prev = -1, i, j, - s = "M", sWithRegion, - xp, yp, dx, dy, dd, diff, diffx2, - xOffset = $$.isCategorized() ? 0.5 : 0, - xValue, yValue, - regions = []; - - function isWithinRegions(x, regions) { - var i; - for (i = 0; i < regions.length; i++) { - if (regions[i].start < x && x <= regions[i].end) { return true; } - } - return false; - } - - // Check start/end of regions - if (isDefined(_regions)) { - for (i = 0; i < _regions.length; i++) { - regions[i] = {}; - if (isUndefined(_regions[i].start)) { - regions[i].start = d[0].x; - } else { - regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start; - } - if (isUndefined(_regions[i].end)) { - regions[i].end = d[d.length - 1].x; - } else { - regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end; - } - } - } - - // Set scales - xValue = config.axis_rotated ? function (d) { return y(d.value); } : function (d) { return x(d.x); }; - yValue = config.axis_rotated ? function (d) { return x(d.x); } : function (d) { return y(d.value); }; - - // Define svg generator function for region - function generateM(points) { - return 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1]; - } - if ($$.isTimeSeries()) { - sWithRegion = function (d0, d1, j, diff) { - var x0 = d0.x.getTime(), x_diff = d1.x - d0.x, - xv0 = new Date(x0 + x_diff * j), - xv1 = new Date(x0 + x_diff * (j + diff)), - points; - if (config.axis_rotated) { - points = [[y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)]]; - } else { - points = [[x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))]]; - } - return generateM(points); - }; - } else { - sWithRegion = function (d0, d1, j, diff) { - var points; - if (config.axis_rotated) { - points = [[y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))]]; - } else { - points = [[x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))]]; - } - return generateM(points); - }; - } - - // Generate - for (i = 0; i < d.length; i++) { - - // Draw as normal - if (isUndefined(regions) || ! isWithinRegions(d[i].x, regions)) { - s += " " + xValue(d[i]) + " " + yValue(d[i]); - } - // Draw with region // TODO: Fix for horizotal charts - else { - xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries()); - yp = $$.getScale(d[i - 1].value, d[i].value); - - dx = x(d[i].x) - x(d[i - 1].x); - dy = y(d[i].value) - y(d[i - 1].value); - dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); - diff = 2 / dd; - diffx2 = diff * 2; - - for (j = diff; j <= 1; j += diffx2) { - s += sWithRegion(d[i - 1], d[i], j, diff); - } - } - prev = d[i].x; - } - - return s; - }; - - - c3_chart_internal_fn.updateArea = function (durationForExit) { - var $$ = this, d3 = $$.d3; - $$.mainArea = $$.main.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area) - .data($$.lineData.bind($$)); - $$.mainArea.enter().append('path') - .attr("class", $$.classArea.bind($$)) - .style("fill", $$.color) - .style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; }); - $$.mainArea - .style("opacity", $$.orgAreaOpacity); - $$.mainArea.exit().transition().duration(durationForExit) - .style('opacity', 0) - .remove(); - }; - c3_chart_internal_fn.redrawArea = function (drawArea, withTransition) { - return [ - (withTransition ? this.mainArea.transition(Math.random().toString()) : this.mainArea) - .attr("d", drawArea) - .style("fill", this.color) - .style("opacity", this.orgAreaOpacity) - ]; - }; - c3_chart_internal_fn.generateDrawArea = function (areaIndices, isSub) { - var $$ = this, config = $$.config, area = $$.d3.svg.area(), - getPoints = $$.generateGetAreaPoints(areaIndices, isSub), - yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, - xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); }, - value0 = function (d, i) { - return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id)); - }, - value1 = function (d, i) { - return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value); - }; - - area = config.axis_rotated ? area.x0(value0).x1(value1).y(xValue) : area.x(xValue).y0(config.area_above ? 0 : value0).y1(value1); - if (!config.line_connectNull) { - area = area.defined(function (d) { return d.value !== null; }); - } - - return function (d) { - var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, - x0 = 0, y0 = 0, path; - if ($$.isAreaType(d)) { - if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } - path = area.interpolate($$.getInterpolate(d))(values); - } else { - if (values[0]) { - x0 = $$.x(values[0].x); - y0 = $$.getYScale(d.id)(values[0].value); - } - path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0; - } - return path ? path : "M 0 0"; - }; - }; - c3_chart_internal_fn.getAreaBaseValue = function () { - return 0; - }; - c3_chart_internal_fn.generateGetAreaPoints = function (areaIndices, isSub) { // partial duplication of generateGetBarPoints - var $$ = this, config = $$.config, - areaTargetsNum = areaIndices.__max__ + 1, - x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub), - y = $$.getShapeY(!!isSub), - areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub), - yScale = isSub ? $$.getSubYScale : $$.getYScale; - return function (d, i) { - var y0 = yScale.call($$, d.id)(0), - offset = areaOffset(d, i) || y0, // offset is for stacked area chart - posX = x(d), posY = y(d); - // fix posY not to overflow opposite quadrant - if (config.axis_rotated) { - if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } - } - // 1 point that marks the area position - return [ - [posX, offset], - [posX, posY - (y0 - offset)], - [posX, posY - (y0 - offset)], // needed for compatibility - [posX, offset] // needed for compatibility - ]; - }; - }; - - - c3_chart_internal_fn.updateCircle = function () { - var $$ = this; - $$.mainCircle = $$.main.selectAll('.' + CLASS.circles).selectAll('.' + CLASS.circle) - .data($$.lineOrScatterData.bind($$)); - $$.mainCircle.enter().append("circle") - .attr("class", $$.classCircle.bind($$)) - .attr("r", $$.pointR.bind($$)) - .style("fill", $$.color); - $$.mainCircle - .style("opacity", $$.initialOpacityForCircle.bind($$)); - $$.mainCircle.exit().remove(); - }; - c3_chart_internal_fn.redrawCircle = function (cx, cy, withTransition) { - var selectedCircles = this.main.selectAll('.' + CLASS.selectedCircle); - return [ - (withTransition ? this.mainCircle.transition(Math.random().toString()) : this.mainCircle) - .style('opacity', this.opacityForCircle.bind(this)) - .style("fill", this.color) - .attr("cx", cx) - .attr("cy", cy), - (withTransition ? selectedCircles.transition(Math.random().toString()) : selectedCircles) - .attr("cx", cx) - .attr("cy", cy) - ]; - }; - c3_chart_internal_fn.circleX = function (d) { - return d.x || d.x === 0 ? this.x(d.x) : null; - }; - c3_chart_internal_fn.updateCircleY = function () { - var $$ = this, lineIndices, getPoints; - if ($$.config.data_groups.length > 0) { - lineIndices = $$.getShapeIndices($$.isLineType), - getPoints = $$.generateGetLinePoints(lineIndices); - $$.circleY = function (d, i) { - return getPoints(d, i)[0][1]; - }; - } else { - $$.circleY = function (d) { - return $$.getYScale(d.id)(d.value); - }; - } - }; - c3_chart_internal_fn.getCircles = function (i, id) { - var $$ = this; - return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : '')); - }; - c3_chart_internal_fn.expandCircles = function (i, id, reset) { - var $$ = this, - r = $$.pointExpandedR.bind($$); - if (reset) { $$.unexpandCircles(); } - $$.getCircles(i, id) - .classed(CLASS.EXPANDED, true) - .attr('r', r); - }; - c3_chart_internal_fn.unexpandCircles = function (i) { - var $$ = this, - r = $$.pointR.bind($$); - $$.getCircles(i) - .filter(function () { return $$.d3.select(this).classed(CLASS.EXPANDED); }) - .classed(CLASS.EXPANDED, false) - .attr('r', r); - }; - c3_chart_internal_fn.pointR = function (d) { - var $$ = this, config = $$.config; - return $$.isStepType(d) ? 0 : (isFunction(config.point_r) ? config.point_r(d) : config.point_r); - }; - c3_chart_internal_fn.pointExpandedR = function (d) { - var $$ = this, config = $$.config; - return config.point_focus_expand_enabled ? (config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75) : $$.pointR(d); - }; - c3_chart_internal_fn.pointSelectR = function (d) { - var $$ = this, config = $$.config; - return isFunction(config.point_select_r) ? config.point_select_r(d) : ((config.point_select_r) ? config.point_select_r : $$.pointR(d) * 4); - }; - c3_chart_internal_fn.isWithinCircle = function (that, r) { - var d3 = this.d3, - mouse = d3.mouse(that), d3_this = d3.select(that), - cx = +d3_this.attr("cx"), cy = +d3_this.attr("cy"); - return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r; - }; - c3_chart_internal_fn.isWithinStep = function (that, y) { - return Math.abs(y - this.d3.mouse(that)[1]) < 30; - }; - - c3_chart_internal_fn.initBar = function () { - var $$ = this; - $$.main.select('.' + CLASS.chart).append("g") - .attr("class", CLASS.chartBars); - }; - c3_chart_internal_fn.updateTargetsForBar = function (targets) { - var $$ = this, config = $$.config, - mainBarUpdate, mainBarEnter, - classChartBar = $$.classChartBar.bind($$), - classBars = $$.classBars.bind($$), - classFocus = $$.classFocus.bind($$); - mainBarUpdate = $$.main.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar) - .data(targets) - .attr('class', function (d) { return classChartBar(d) + classFocus(d); }); - mainBarEnter = mainBarUpdate.enter().append('g') - .attr('class', classChartBar) - .style('opacity', 0) - .style("pointer-events", "none"); - // Bars for each data - mainBarEnter.append('g') - .attr("class", classBars) - .style("cursor", function (d) { return config.data_selection_isselectable(d) ? "pointer" : null; }); - - }; - c3_chart_internal_fn.updateBar = function (durationForExit) { - var $$ = this, - barData = $$.barData.bind($$), - classBar = $$.classBar.bind($$), - initialOpacity = $$.initialOpacity.bind($$), - color = function (d) { return $$.color(d.id); }; - $$.mainBar = $$.main.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar) - .data(barData); - $$.mainBar.enter().append('path') - .attr("class", classBar) - .style("stroke", color) - .style("fill", color); - $$.mainBar - .style("opacity", initialOpacity); - $$.mainBar.exit().transition().duration(durationForExit) - .style('opacity', 0) - .remove(); - }; - c3_chart_internal_fn.redrawBar = function (drawBar, withTransition) { - return [ - (withTransition ? this.mainBar.transition(Math.random().toString()) : this.mainBar) - .attr('d', drawBar) - .style("fill", this.color) - .style("opacity", 1) - ]; - }; - c3_chart_internal_fn.getBarW = function (axis, barTargetsNum) { - var $$ = this, config = $$.config, - w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickInterval() * config.bar_width_ratio) / barTargetsNum : 0; - return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w; - }; - c3_chart_internal_fn.getBars = function (i, id) { - var $$ = this; - return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : '')); - }; - c3_chart_internal_fn.expandBars = function (i, id, reset) { - var $$ = this; - if (reset) { $$.unexpandBars(); } - $$.getBars(i, id).classed(CLASS.EXPANDED, true); - }; - c3_chart_internal_fn.unexpandBars = function (i) { - var $$ = this; - $$.getBars(i).classed(CLASS.EXPANDED, false); - }; - c3_chart_internal_fn.generateDrawBar = function (barIndices, isSub) { - var $$ = this, config = $$.config, - getPoints = $$.generateGetBarPoints(barIndices, isSub); - return function (d, i) { - // 4 points that make a bar - var points = getPoints(d, i); - - // switch points if axis is rotated, not applicable for sub chart - var indexX = config.axis_rotated ? 1 : 0; - var indexY = config.axis_rotated ? 0 : 1; - - var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + - 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + - 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + - 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + - 'z'; - - return path; - }; - }; - c3_chart_internal_fn.generateGetBarPoints = function (barIndices, isSub) { - var $$ = this, - axis = isSub ? $$.subXAxis : $$.xAxis, - barTargetsNum = barIndices.__max__ + 1, - barW = $$.getBarW(axis, barTargetsNum), - barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub), - barY = $$.getShapeY(!!isSub), - barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub), - yScale = isSub ? $$.getSubYScale : $$.getYScale; - return function (d, i) { - var y0 = yScale.call($$, d.id)(0), - offset = barOffset(d, i) || y0, // offset is for stacked bar chart - posX = barX(d), posY = barY(d); - // fix posY not to overflow opposite quadrant - if ($$.config.axis_rotated) { - if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } - } - // 4 points that make a bar - return [ - [posX, offset], - [posX, posY - (y0 - offset)], - [posX + barW, posY - (y0 - offset)], - [posX + barW, offset] - ]; - }; - }; - c3_chart_internal_fn.isWithinBar = function (that) { - var mouse = this.d3.mouse(that), box = that.getBoundingClientRect(), - seg0 = that.pathSegList.getItem(0), seg1 = that.pathSegList.getItem(1), - x = Math.min(seg0.x, seg1.x), y = Math.min(seg0.y, seg1.y), - w = box.width, h = box.height, offset = 2, - sx = x - offset, ex = x + w + offset, sy = y + h + offset, ey = y - offset; - return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy; - }; - - c3_chart_internal_fn.initText = function () { - var $$ = this; - $$.main.select('.' + CLASS.chart).append("g") - .attr("class", CLASS.chartTexts); - $$.mainText = $$.d3.selectAll([]); - }; - c3_chart_internal_fn.updateTargetsForText = function (targets) { - var $$ = this, mainTextUpdate, mainTextEnter, - classChartText = $$.classChartText.bind($$), - classTexts = $$.classTexts.bind($$), - classFocus = $$.classFocus.bind($$); - mainTextUpdate = $$.main.select('.' + CLASS.chartTexts).selectAll('.' + CLASS.chartText) - .data(targets) - .attr('class', function (d) { return classChartText(d) + classFocus(d); }); - mainTextEnter = mainTextUpdate.enter().append('g') - .attr('class', classChartText) - .style('opacity', 0) - .style("pointer-events", "none"); - mainTextEnter.append('g') - .attr('class', classTexts); - }; - c3_chart_internal_fn.updateText = function (durationForExit) { - var $$ = this, config = $$.config, - barOrLineData = $$.barOrLineData.bind($$), - classText = $$.classText.bind($$); - $$.mainText = $$.main.selectAll('.' + CLASS.texts).selectAll('.' + CLASS.text) - .data(barOrLineData); - $$.mainText.enter().append('text') - .attr("class", classText) - .attr('text-anchor', function (d) { return config.axis_rotated ? (d.value < 0 ? 'end' : 'start') : 'middle'; }) - .style("stroke", 'none') - .style("fill", function (d) { return $$.color(d); }) - .style("fill-opacity", 0); - $$.mainText - .text(function (d, i, j) { return $$.dataLabelFormat(d.id)(d.value, d.id, i, j); }); - $$.mainText.exit() - .transition().duration(durationForExit) - .style('fill-opacity', 0) - .remove(); - }; - c3_chart_internal_fn.redrawText = function (xForText, yForText, forFlow, withTransition) { - return [ - (withTransition ? this.mainText.transition() : this.mainText) - .attr('x', xForText) - .attr('y', yForText) - .style("fill", this.color) - .style("fill-opacity", forFlow ? 0 : this.opacityForText.bind(this)) - ]; - }; - c3_chart_internal_fn.getTextRect = function (text, cls, element) { - var dummy = this.d3.select('body').append('div').classed('c3', true), - svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0), - font = this.d3.select(element).style('font'), - rect; - svg.selectAll('.dummy') - .data([text]) - .enter().append('text') - .classed(cls ? cls : "", true) - .style('font', font) - .text(text) - .each(function () { rect = this.getBoundingClientRect(); }); - dummy.remove(); - return rect; - }; - c3_chart_internal_fn.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) { - var $$ = this, - getAreaPoints = $$.generateGetAreaPoints(areaIndices, false), - getBarPoints = $$.generateGetBarPoints(barIndices, false), - getLinePoints = $$.generateGetLinePoints(lineIndices, false), - getter = forX ? $$.getXForText : $$.getYForText; - return function (d, i) { - var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints; - return getter.call($$, getPoints(d, i), d, this); - }; - }; - c3_chart_internal_fn.getXForText = function (points, d, textElement) { - var $$ = this, - box = textElement.getBoundingClientRect(), xPos, padding; - if ($$.config.axis_rotated) { - padding = $$.isBarType(d) ? 4 : 6; - xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1); - } else { - xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0]; - } - // show labels regardless of the domain if value is null - if (d.value === null) { - if (xPos > $$.width) { - xPos = $$.width - box.width; - } else if (xPos < 0) { - xPos = 4; - } - } - return xPos; - }; - c3_chart_internal_fn.getYForText = function (points, d, textElement) { - var $$ = this, - box = textElement.getBoundingClientRect(), - yPos; - if ($$.config.axis_rotated) { - yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2; - } else { - yPos = points[2][1]; - if (d.value < 0 || (d.value === 0 && !$$.hasPositiveValue)) { - yPos += box.height; - if ($$.isBarType(d) && $$.isSafari()) { - yPos -= 3; - } - else if (!$$.isBarType(d) && $$.isChrome()) { - yPos += 3; - } - } else { - yPos += $$.isBarType(d) ? -3 : -6; - } - } - // show labels regardless of the domain if value is null - if (d.value === null && !$$.config.axis_rotated) { - if (yPos < box.height) { - yPos = box.height; - } else if (yPos > this.height) { - yPos = this.height - 4; - } - } - return yPos; - }; - - c3_chart_internal_fn.setTargetType = function (targetIds, type) { - var $$ = this, config = $$.config; - $$.mapToTargetIds(targetIds).forEach(function (id) { - $$.withoutFadeIn[id] = (type === config.data_types[id]); - config.data_types[id] = type; - }); - if (!targetIds) { - config.data_type = type; - } - }; - c3_chart_internal_fn.hasType = function (type, targets) { - var $$ = this, types = $$.config.data_types, has = false; - targets = targets || $$.data.targets; - if (targets && targets.length) { - targets.forEach(function (target) { - var t = types[target.id]; - if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) { - has = true; - } - }); - } else if (Object.keys(types).length) { - Object.keys(types).forEach(function (id) { - if (types[id] === type) { has = true; } - }); - } else { - has = $$.config.data_type === type; - } - return has; - }; - c3_chart_internal_fn.hasArcType = function (targets) { - return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets); - }; - c3_chart_internal_fn.isLineType = function (d) { - var config = this.config, id = isString(d) ? d : d.id; - return !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0; - }; - c3_chart_internal_fn.isStepType = function (d) { - var id = isString(d) ? d : d.id; - return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0; - }; - c3_chart_internal_fn.isSplineType = function (d) { - var id = isString(d) ? d : d.id; - return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0; - }; - c3_chart_internal_fn.isAreaType = function (d) { - var id = isString(d) ? d : d.id; - return ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0; - }; - c3_chart_internal_fn.isBarType = function (d) { - var id = isString(d) ? d : d.id; - return this.config.data_types[id] === 'bar'; - }; - c3_chart_internal_fn.isScatterType = function (d) { - var id = isString(d) ? d : d.id; - return this.config.data_types[id] === 'scatter'; - }; - c3_chart_internal_fn.isPieType = function (d) { - var id = isString(d) ? d : d.id; - return this.config.data_types[id] === 'pie'; - }; - c3_chart_internal_fn.isGaugeType = function (d) { - var id = isString(d) ? d : d.id; - return this.config.data_types[id] === 'gauge'; - }; - c3_chart_internal_fn.isDonutType = function (d) { - var id = isString(d) ? d : d.id; - return this.config.data_types[id] === 'donut'; - }; - c3_chart_internal_fn.isArcType = function (d) { - return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d); - }; - c3_chart_internal_fn.lineData = function (d) { - return this.isLineType(d) ? [d] : []; - }; - c3_chart_internal_fn.arcData = function (d) { - return this.isArcType(d.data) ? [d] : []; - }; - /* not used - function scatterData(d) { - return isScatterType(d) ? d.values : []; - } - */ - c3_chart_internal_fn.barData = function (d) { - return this.isBarType(d) ? d.values : []; - }; - c3_chart_internal_fn.lineOrScatterData = function (d) { - return this.isLineType(d) || this.isScatterType(d) ? d.values : []; - }; - c3_chart_internal_fn.barOrLineData = function (d) { - return this.isBarType(d) || this.isLineType(d) ? d.values : []; - }; - c3_chart_internal_fn.isInterpolationType = function (type) { - return ['linear', 'linear-closed', 'basis', 'basis-open', 'basis-closed', 'bundle', 'cardinal', 'cardinal-open', 'cardinal-closed', 'monotone'].indexOf(type) >= 0; - }; - - c3_chart_internal_fn.initGrid = function () { - var $$ = this, config = $$.config, d3 = $$.d3; - $$.grid = $$.main.append('g') - .attr("clip-path", $$.clipPathForGrid) - .attr('class', CLASS.grid); - if (config.grid_x_show) { - $$.grid.append("g").attr("class", CLASS.xgrids); - } - if (config.grid_y_show) { - $$.grid.append('g').attr('class', CLASS.ygrids); - } - if (config.grid_focus_show) { - $$.grid.append('g') - .attr("class", CLASS.xgridFocus) - .append('line') - .attr('class', CLASS.xgridFocus); - } - $$.xgrid = d3.selectAll([]); - if (!config.grid_lines_front) { $$.initGridLines(); } - }; - c3_chart_internal_fn.initGridLines = function () { - var $$ = this, d3 = $$.d3; - $$.gridLines = $$.main.append('g') - .attr("clip-path", $$.clipPathForGrid) - .attr('class', CLASS.grid + ' ' + CLASS.gridLines); - $$.gridLines.append('g').attr("class", CLASS.xgridLines); - $$.gridLines.append('g').attr('class', CLASS.ygridLines); - $$.xgridLines = d3.selectAll([]); - }; - c3_chart_internal_fn.updateXGrid = function (withoutUpdate) { - var $$ = this, config = $$.config, d3 = $$.d3, - xgridData = $$.generateGridData(config.grid_x_type, $$.x), - tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0; - - $$.xgridAttr = config.axis_rotated ? { - 'x1': 0, - 'x2': $$.width, - 'y1': function (d) { return $$.x(d) - tickOffset; }, - 'y2': function (d) { return $$.x(d) - tickOffset; } - } : { - 'x1': function (d) { return $$.x(d) + tickOffset; }, - 'x2': function (d) { return $$.x(d) + tickOffset; }, - 'y1': 0, - 'y2': $$.height - }; - - $$.xgrid = $$.main.select('.' + CLASS.xgrids).selectAll('.' + CLASS.xgrid) - .data(xgridData); - $$.xgrid.enter().append('line').attr("class", CLASS.xgrid); - if (!withoutUpdate) { - $$.xgrid.attr($$.xgridAttr) - .style("opacity", function () { return +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1') === (config.axis_rotated ? $$.height : 0) ? 0 : 1; }); - } - $$.xgrid.exit().remove(); - }; - - c3_chart_internal_fn.updateYGrid = function () { - var $$ = this, config = $$.config, - gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks); - $$.ygrid = $$.main.select('.' + CLASS.ygrids).selectAll('.' + CLASS.ygrid) - .data(gridValues); - $$.ygrid.enter().append('line') - .attr('class', CLASS.ygrid); - $$.ygrid.attr("x1", config.axis_rotated ? $$.y : 0) - .attr("x2", config.axis_rotated ? $$.y : $$.width) - .attr("y1", config.axis_rotated ? 0 : $$.y) - .attr("y2", config.axis_rotated ? $$.height : $$.y); - $$.ygrid.exit().remove(); - $$.smoothLines($$.ygrid, 'grid'); - }; - - c3_chart_internal_fn.gridTextAnchor = function (d) { - return d.position ? d.position : "end"; - }; - c3_chart_internal_fn.gridTextDx = function (d) { - return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4; - }; - c3_chart_internal_fn.xGridTextX = function (d) { - return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0; - }; - c3_chart_internal_fn.yGridTextX = function (d) { - return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width; - }; - c3_chart_internal_fn.updateGrid = function (duration) { - var $$ = this, main = $$.main, config = $$.config, - xgridLine, ygridLine, yv; - - // hide if arc type - $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); - - main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden"); - if (config.grid_x_show) { - $$.updateXGrid(); - } - $$.xgridLines = main.select('.' + CLASS.xgridLines).selectAll('.' + CLASS.xgridLine) - .data(config.grid_x_lines); - // enter - xgridLine = $$.xgridLines.enter().append('g') - .attr("class", function (d) { return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : ''); }); - xgridLine.append('line') - .style("opacity", 0); - xgridLine.append('text') - .attr("text-anchor", $$.gridTextAnchor) - .attr("transform", config.axis_rotated ? "" : "rotate(-90)") - .attr('dx', $$.gridTextDx) - .attr('dy', -5) - .style("opacity", 0); - // udpate - // done in d3.transition() of the end of this function - // exit - $$.xgridLines.exit().transition().duration(duration) - .style("opacity", 0) - .remove(); - - // Y-Grid - if (config.grid_y_show) { - $$.updateYGrid(); - } - $$.ygridLines = main.select('.' + CLASS.ygridLines).selectAll('.' + CLASS.ygridLine) - .data(config.grid_y_lines); - // enter - ygridLine = $$.ygridLines.enter().append('g') - .attr("class", function (d) { return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : ''); }); - ygridLine.append('line') - .style("opacity", 0); - ygridLine.append('text') - .attr("text-anchor", $$.gridTextAnchor) - .attr("transform", config.axis_rotated ? "rotate(-90)" : "") - .attr('dx', $$.gridTextDx) - .attr('dy', -5) - .style("opacity", 0); - // update - yv = $$.yv.bind($$); - $$.ygridLines.select('line') - .transition().duration(duration) - .attr("x1", config.axis_rotated ? yv : 0) - .attr("x2", config.axis_rotated ? yv : $$.width) - .attr("y1", config.axis_rotated ? 0 : yv) - .attr("y2", config.axis_rotated ? $$.height : yv) - .style("opacity", 1); - $$.ygridLines.select('text') - .transition().duration(duration) - .attr("x", config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$)) - .attr("y", yv) - .text(function (d) { return d.text; }) - .style("opacity", 1); - // exit - $$.ygridLines.exit().transition().duration(duration) - .style("opacity", 0) - .remove(); - }; - c3_chart_internal_fn.redrawGrid = function (withTransition) { - var $$ = this, config = $$.config, xv = $$.xv.bind($$), - lines = $$.xgridLines.select('line'), - texts = $$.xgridLines.select('text'); - return [ - (withTransition ? lines.transition() : lines) - .attr("x1", config.axis_rotated ? 0 : xv) - .attr("x2", config.axis_rotated ? $$.width : xv) - .attr("y1", config.axis_rotated ? xv : 0) - .attr("y2", config.axis_rotated ? xv : $$.height) - .style("opacity", 1), - (withTransition ? texts.transition() : texts) - .attr("x", config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$)) - .attr("y", xv) - .text(function (d) { return d.text; }) - .style("opacity", 1) - ]; - }; - c3_chart_internal_fn.showXGridFocus = function (selectedData) { - var $$ = this, config = $$.config, - dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }), - focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus), - xx = $$.xx.bind($$); - if (! config.tooltip_show) { return; } - // Hide when scatter plot exists - if ($$.hasType('scatter') || $$.hasArcType()) { return; } - focusEl - .style("visibility", "visible") - .data([dataToShow[0]]) - .attr(config.axis_rotated ? 'y1' : 'x1', xx) - .attr(config.axis_rotated ? 'y2' : 'x2', xx); - $$.smoothLines(focusEl, 'grid'); - }; - c3_chart_internal_fn.hideXGridFocus = function () { - this.main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden"); - }; - c3_chart_internal_fn.updateXgridFocus = function () { - var $$ = this, config = $$.config; - $$.main.select('line.' + CLASS.xgridFocus) - .attr("x1", config.axis_rotated ? 0 : -10) - .attr("x2", config.axis_rotated ? $$.width : -10) - .attr("y1", config.axis_rotated ? -10 : 0) - .attr("y2", config.axis_rotated ? -10 : $$.height); - }; - c3_chart_internal_fn.generateGridData = function (type, scale) { - var $$ = this, - gridData = [], xDomain, firstYear, lastYear, i, - tickNum = $$.main.select("." + CLASS.axisX).selectAll('.tick').size(); - if (type === 'year') { - xDomain = $$.getXDomain(); - firstYear = xDomain[0].getFullYear(); - lastYear = xDomain[1].getFullYear(); - for (i = firstYear; i <= lastYear; i++) { - gridData.push(new Date(i + '-01-01 00:00:00')); - } - } else { - gridData = scale.ticks(10); - if (gridData.length > tickNum) { // use only int - gridData = gridData.filter(function (d) { return ("" + d).indexOf('.') < 0; }); - } - } - return gridData; - }; - c3_chart_internal_fn.getGridFilterToRemove = function (params) { - return params ? function (line) { - var found = false; - [].concat(params).forEach(function (param) { - if ((('value' in param && line.value === param.value) || ('class' in param && line['class'] === param['class']))) { - found = true; - } - }); - return found; - } : function () { return true; }; - }; - c3_chart_internal_fn.removeGridLines = function (params, forX) { - var $$ = this, config = $$.config, - toRemove = $$.getGridFilterToRemove(params), - toShow = function (line) { return !toRemove(line); }, - classLines = forX ? CLASS.xgridLines : CLASS.ygridLines, - classLine = forX ? CLASS.xgridLine : CLASS.ygridLine; - $$.main.select('.' + classLines).selectAll('.' + classLine).filter(toRemove) - .transition().duration(config.transition_duration) - .style('opacity', 0).remove(); - if (forX) { - config.grid_x_lines = config.grid_x_lines.filter(toShow); - } else { - config.grid_y_lines = config.grid_y_lines.filter(toShow); - } - }; - - c3_chart_internal_fn.initTooltip = function () { - var $$ = this, config = $$.config, i; - $$.tooltip = $$.selectChart - .style("position", "relative") - .append("div") - .attr('class', CLASS.tooltipContainer) - .style("position", "absolute") - .style("pointer-events", "none") - .style("display", "none"); - // Show tooltip if needed - if (config.tooltip_init_show) { - if ($$.isTimeSeries() && isString(config.tooltip_init_x)) { - config.tooltip_init_x = $$.parseDate(config.tooltip_init_x); - for (i = 0; i < $$.data.targets[0].values.length; i++) { - if (($$.data.targets[0].values[i].x - config.tooltip_init_x) === 0) { break; } - } - config.tooltip_init_x = i; - } - $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) { - return $$.addName(d.values[config.tooltip_init_x]); - }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color)); - $$.tooltip.style("top", config.tooltip_init_position.top) - .style("left", config.tooltip_init_position.left) - .style("display", "block"); - } - }; - c3_chart_internal_fn.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) { - var $$ = this, config = $$.config, - titleFormat = config.tooltip_format_title || defaultTitleFormat, - nameFormat = config.tooltip_format_name || function (name) { return name; }, - valueFormat = config.tooltip_format_value || defaultValueFormat, - text, i, title, value, name, bgcolor, - orderAsc = $$.isOrderAsc(); - - if (config.data_groups.length === 0) { - d.sort(function(a, b){ - var v1 = a ? a.value : null, v2 = b ? b.value : null; - return orderAsc ? v1 - v2 : v2 - v1; - }); - } else { - var ids = $$.orderTargets($$.data.targets).map(function (i) { - return i.id; - }); - d.sort(function(a, b) { - var v1 = a ? a.value : null, v2 = b ? b.value : null; - if (v1 > 0 && v2 > 0) { - v1 = a ? ids.indexOf(a.id) : null; - v2 = b ? ids.indexOf(b.id) : null; - } - return orderAsc ? v1 - v2 : v2 - v1; - }); - } - - for (i = 0; i < d.length; i++) { - if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; } - - if (! text) { - title = sanitise(titleFormat ? titleFormat(d[i].x) : d[i].x); - text = "" + (title || title === 0 ? "" : ""); - } - - value = sanitise(valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d)); - if (value !== undefined) { - // Skip elements when their name is set to null - if (d[i].name === null) { continue; } - name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)); - bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); - - text += ""; - text += ""; - text += ""; - text += ""; - } - } - return text + "
" + title + "
" + name + "" + value + "
"; - }; - c3_chart_internal_fn.tooltipPosition = function (dataToShow, tWidth, tHeight, element) { - var $$ = this, config = $$.config, d3 = $$.d3; - var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight; - var forArc = $$.hasArcType(), - mouse = d3.mouse(element); - // Determin tooltip position - if (forArc) { - tooltipLeft = (($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2) + mouse[0]; - tooltipTop = ($$.height / 2) + mouse[1] + 20; - } else { - svgLeft = $$.getSvgLeft(true); - if (config.axis_rotated) { - tooltipLeft = svgLeft + mouse[0] + 100; - tooltipRight = tooltipLeft + tWidth; - chartRight = $$.currentWidth - $$.getCurrentPaddingRight(); - tooltipTop = $$.x(dataToShow[0].x) + 20; - } else { - tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20; - tooltipRight = tooltipLeft + tWidth; - chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight(); - tooltipTop = mouse[1] + 15; - } - - if (tooltipRight > chartRight) { - // 20 is needed for Firefox to keep tooltip width - tooltipLeft -= tooltipRight - chartRight + 20; - } - if (tooltipTop + tHeight > $$.currentHeight) { - tooltipTop -= tHeight + 30; - } - } - if (tooltipTop < 0) { - tooltipTop = 0; - } - return {top: tooltipTop, left: tooltipLeft}; - }; - c3_chart_internal_fn.showTooltip = function (selectedData, element) { - var $$ = this, config = $$.config; - var tWidth, tHeight, position; - var forArc = $$.hasArcType(), - dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }), - positionFunction = config.tooltip_position || c3_chart_internal_fn.tooltipPosition; - if (dataToShow.length === 0 || !config.tooltip_show) { - return; - } - $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block"); - - // Get tooltip dimensions - tWidth = $$.tooltip.property('offsetWidth'); - tHeight = $$.tooltip.property('offsetHeight'); - - position = positionFunction.call(this, dataToShow, tWidth, tHeight, element); - // Set tooltip - $$.tooltip - .style("top", position.top + "px") - .style("left", position.left + 'px'); - }; - c3_chart_internal_fn.hideTooltip = function () { - this.tooltip.style("display", "none"); - }; - - c3_chart_internal_fn.initLegend = function () { - var $$ = this; - $$.legendItemTextBox = {}; - $$.legendHasRendered = false; - $$.legend = $$.svg.append("g").attr("transform", $$.getTranslate('legend')); - if (!$$.config.legend_show) { - $$.legend.style('visibility', 'hidden'); - $$.hiddenLegendIds = $$.mapToIds($$.data.targets); - return; - } - // MEMO: call here to update legend box and tranlate for all - // MEMO: translate will be upated by this, so transform not needed in updateLegend() - $$.updateLegendWithDefaults(); - }; - c3_chart_internal_fn.updateLegendWithDefaults = function () { - var $$ = this; - $$.updateLegend($$.mapToIds($$.data.targets), {withTransform: false, withTransitionForTransform: false, withTransition: false}); - }; - c3_chart_internal_fn.updateSizeForLegend = function (legendHeight, legendWidth) { - var $$ = this, config = $$.config, insetLegendPosition = { - top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y, - left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5 - }; - - $$.margin3 = { - top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight, - right: NaN, - bottom: 0, - left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0 - }; - }; - c3_chart_internal_fn.transformLegend = function (withTransition) { - var $$ = this; - (withTransition ? $$.legend.transition() : $$.legend).attr("transform", $$.getTranslate('legend')); - }; - c3_chart_internal_fn.updateLegendStep = function (step) { - this.legendStep = step; - }; - c3_chart_internal_fn.updateLegendItemWidth = function (w) { - this.legendItemWidth = w; - }; - c3_chart_internal_fn.updateLegendItemHeight = function (h) { - this.legendItemHeight = h; - }; - c3_chart_internal_fn.getLegendWidth = function () { - var $$ = this; - return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0; - }; - c3_chart_internal_fn.getLegendHeight = function () { - var $$ = this, h = 0; - if ($$.config.legend_show) { - if ($$.isLegendRight) { - h = $$.currentHeight; - } else { - h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1); - } - } - return h; - }; - c3_chart_internal_fn.opacityForLegend = function (legendItem) { - return legendItem.classed(CLASS.legendItemHidden) ? null : 1; - }; - c3_chart_internal_fn.opacityForUnfocusedLegend = function (legendItem) { - return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3; - }; - c3_chart_internal_fn.toggleFocusLegend = function (targetIds, focus) { - var $$ = this; - targetIds = $$.mapToTargetIds(targetIds); - $$.legend.selectAll('.' + CLASS.legendItem) - .filter(function (id) { return targetIds.indexOf(id) >= 0; }) - .classed(CLASS.legendItemFocused, focus) - .transition().duration(100) - .style('opacity', function () { - var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend; - return opacity.call($$, $$.d3.select(this)); - }); - }; - c3_chart_internal_fn.revertLegend = function () { - var $$ = this, d3 = $$.d3; - $$.legend.selectAll('.' + CLASS.legendItem) - .classed(CLASS.legendItemFocused, false) - .transition().duration(100) - .style('opacity', function () { return $$.opacityForLegend(d3.select(this)); }); - }; - c3_chart_internal_fn.showLegend = function (targetIds) { - var $$ = this, config = $$.config; - if (!config.legend_show) { - config.legend_show = true; - $$.legend.style('visibility', 'visible'); - if (!$$.legendHasRendered) { - $$.updateLegendWithDefaults(); - } - } - $$.removeHiddenLegendIds(targetIds); - $$.legend.selectAll($$.selectorLegends(targetIds)) - .style('visibility', 'visible') - .transition() - .style('opacity', function () { return $$.opacityForLegend($$.d3.select(this)); }); - }; - c3_chart_internal_fn.hideLegend = function (targetIds) { - var $$ = this, config = $$.config; - if (config.legend_show && isEmpty(targetIds)) { - config.legend_show = false; - $$.legend.style('visibility', 'hidden'); - } - $$.addHiddenLegendIds(targetIds); - $$.legend.selectAll($$.selectorLegends(targetIds)) - .style('opacity', 0) - .style('visibility', 'hidden'); - }; - c3_chart_internal_fn.clearLegendItemTextBoxCache = function () { - this.legendItemTextBox = {}; - }; - c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { - var $$ = this, config = $$.config; - var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile; - var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5; - var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0; - var withTransition, withTransitionForTransform; - var texts, rects, tiles, background; - - // Skip elements when their name is set to null - targetIds = targetIds.filter(function(id) { - return !isDefined(config.data_names[id]) || config.data_names[id] !== null; - }); - - options = options || {}; - withTransition = getOption(options, "withTransition", true); - withTransitionForTransform = getOption(options, "withTransitionForTransform", true); - - function getTextBox(textElement, id) { - if (!$$.legendItemTextBox[id]) { - $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem, textElement); - } - return $$.legendItemTextBox[id]; - } - - function updatePositions(textElement, id, index) { - var reset = index === 0, isLast = index === targetIds.length - 1, - box = getTextBox(textElement, id), - itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, - itemHeight = box.height + paddingTop, - itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, - areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), - margin, maxLength; - - // MEMO: care about condifion of step, totalLength - function updateValues(id, withoutStep) { - if (!withoutStep) { - margin = (areaLength - totalLength - itemLength) / 2; - if (margin < posMin) { - margin = (areaLength - itemLength) / 2; - totalLength = 0; - step++; - } - } - steps[id] = step; - margins[step] = $$.isLegendInset ? 10 : margin; - offsets[id] = totalLength; - totalLength += itemLength; - } - - if (reset) { - totalLength = 0; - step = 0; - maxWidth = 0; - maxHeight = 0; - } - - if (config.legend_show && !$$.isLegendToShow(id)) { - widths[id] = heights[id] = steps[id] = offsets[id] = 0; - return; - } - - widths[id] = itemWidth; - heights[id] = itemHeight; - - if (!maxWidth || itemWidth >= maxWidth) { maxWidth = itemWidth; } - if (!maxHeight || itemHeight >= maxHeight) { maxHeight = itemHeight; } - maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth; - - if (config.legend_equally) { - Object.keys(widths).forEach(function (id) { widths[id] = maxWidth; }); - Object.keys(heights).forEach(function (id) { heights[id] = maxHeight; }); - margin = (areaLength - maxLength * targetIds.length) / 2; - if (margin < posMin) { - totalLength = 0; - step = 0; - targetIds.forEach(function (id) { updateValues(id); }); - } - else { - updateValues(id, true); - } - } else { - updateValues(id); - } - } - - if ($$.isLegendInset) { - step = config.legend_inset_step ? config.legend_inset_step : targetIds.length; - $$.updateLegendStep(step); - } - - if ($$.isLegendRight) { - xForLegend = function (id) { return maxWidth * steps[id]; }; - yForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; - } else if ($$.isLegendInset) { - xForLegend = function (id) { return maxWidth * steps[id] + 10; }; - yForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; - } else { - xForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; - yForLegend = function (id) { return maxHeight * steps[id]; }; - } - xForLegendText = function (id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width; }; - yForLegendText = function (id, i) { return yForLegend(id, i) + 9; }; - xForLegendRect = function (id, i) { return xForLegend(id, i); }; - yForLegendRect = function (id, i) { return yForLegend(id, i) - 5; }; - x1ForLegendTile = function (id, i) { return xForLegend(id, i) - 2; }; - x2ForLegendTile = function (id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width; }; - yForLegendTile = function (id, i) { return yForLegend(id, i) + 4; }; - - // Define g for legend area - l = $$.legend.selectAll('.' + CLASS.legendItem) - .data(targetIds) - .enter().append('g') - .attr('class', function (id) { return $$.generateClass(CLASS.legendItem, id); }) - .style('visibility', function (id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden'; }) - .style('cursor', 'pointer') - .on('click', function (id) { - if (config.legend_item_onclick) { - config.legend_item_onclick.call($$, id); - } else { - if ($$.d3.event.altKey) { - $$.api.hide(); - $$.api.show(id); - } else { - $$.api.toggle(id); - $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert(); - } - } - }) - .on('mouseover', function (id) { - if (config.legend_item_onmouseover) { - config.legend_item_onmouseover.call($$, id); - } - else { - $$.d3.select(this).classed(CLASS.legendItemFocused, true); - if (!$$.transiting && $$.isTargetToShow(id)) { - $$.api.focus(id); - } - } - }) - .on('mouseout', function (id) { - if (config.legend_item_onmouseout) { - config.legend_item_onmouseout.call($$, id); - } - else { - $$.d3.select(this).classed(CLASS.legendItemFocused, false); - $$.api.revert(); - } - }); - l.append('text') - .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) - .each(function (id, i) { updatePositions(this, id, i); }) - .style("pointer-events", "none") - .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) - .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText); - l.append('rect') - .attr("class", CLASS.legendItemEvent) - .style('fill-opacity', 0) - .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) - .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); - l.append('line') - .attr('class', CLASS.legendItemTile) - .style('stroke', $$.color) - .style("pointer-events", "none") - .attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200) - .attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) - .attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200) - .attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) - .attr('stroke-width', config.legend_item_tile_height); - - // Set background for inset legend - background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); - if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) { - background = $$.legend.insert('g', '.' + CLASS.legendItem) - .attr("class", CLASS.legendBackground) - .append('rect'); - } - - texts = $$.legend.selectAll('text') - .data(targetIds) - .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) // MEMO: needed for update - .each(function (id, i) { updatePositions(this, id, i); }); - (withTransition ? texts.transition() : texts) - .attr('x', xForLegendText) - .attr('y', yForLegendText); - - rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent) - .data(targetIds); - (withTransition ? rects.transition() : rects) - .attr('width', function (id) { return widths[id]; }) - .attr('height', function (id) { return heights[id]; }) - .attr('x', xForLegendRect) - .attr('y', yForLegendRect); - - tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile) - .data(targetIds); - (withTransition ? tiles.transition() : tiles) - .style('stroke', $$.color) - .attr('x1', x1ForLegendTile) - .attr('y1', yForLegendTile) - .attr('x2', x2ForLegendTile) - .attr('y2', yForLegendTile); - - if (background) { - (withTransition ? background.transition() : background) - .attr('height', $$.getLegendHeight() - 12) - .attr('width', maxWidth * (step + 1) + 10); - } - - // toggle legend state - $$.legend.selectAll('.' + CLASS.legendItem) - .classed(CLASS.legendItemHidden, function (id) { return !$$.isTargetToShow(id); }); - - // Update all to reflect change of legend - $$.updateLegendItemWidth(maxWidth); - $$.updateLegendItemHeight(maxHeight); - $$.updateLegendStep(step); - // Update size and scale - $$.updateSizes(); - $$.updateScales(); - $$.updateSvgSize(); - // Update g positions - $$.transformAll(withTransitionForTransform, transitions); - $$.legendHasRendered = true; - }; - - c3_chart_internal_fn.initTitle = function () { - var $$ = this; - $$.title = $$.svg.append("text") - .text($$.config.title_text) - .attr("class", $$.CLASS.title); - }; - c3_chart_internal_fn.redrawTitle = function () { - var $$ = this; - $$.title - .attr("x", $$.xForTitle.bind($$)) - .attr("y", $$.yForTitle.bind($$)); - }; - c3_chart_internal_fn.xForTitle = function () { - var $$ = this, config = $$.config, position = config.title_position || 'left', x; - if (position.indexOf('right') >= 0) { - x = $$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width - config.title_padding.right; - } else if (position.indexOf('center') >= 0) { - x = ($$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width) / 2; - } else { // left - x = config.title_padding.left; - } - return x; - }; - c3_chart_internal_fn.yForTitle = function () { - var $$ = this; - return $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).height; - }; - c3_chart_internal_fn.getTitlePadding = function() { - var $$ = this; - return $$.yForTitle() + $$.config.title_padding.bottom; - }; - - function Axis(owner) { - API.call(this, owner); - } - - inherit(API, Axis); - - Axis.prototype.init = function init() { - - var $$ = this.owner, config = $$.config, main = $$.main; - $$.axes.x = main.append("g") - .attr("class", CLASS.axis + ' ' + CLASS.axisX) - .attr("clip-path", $$.clipPathForXAxis) - .attr("transform", $$.getTranslate('x')) - .style("visibility", config.axis_x_show ? 'visible' : 'hidden'); - $$.axes.x.append("text") - .attr("class", CLASS.axisXLabel) - .attr("transform", config.axis_rotated ? "rotate(-90)" : "") - .style("text-anchor", this.textAnchorForXAxisLabel.bind(this)); - $$.axes.y = main.append("g") - .attr("class", CLASS.axis + ' ' + CLASS.axisY) - .attr("clip-path", config.axis_y_inner ? "" : $$.clipPathForYAxis) - .attr("transform", $$.getTranslate('y')) - .style("visibility", config.axis_y_show ? 'visible' : 'hidden'); - $$.axes.y.append("text") - .attr("class", CLASS.axisYLabel) - .attr("transform", config.axis_rotated ? "" : "rotate(-90)") - .style("text-anchor", this.textAnchorForYAxisLabel.bind(this)); - - $$.axes.y2 = main.append("g") - .attr("class", CLASS.axis + ' ' + CLASS.axisY2) - // clip-path? - .attr("transform", $$.getTranslate('y2')) - .style("visibility", config.axis_y2_show ? 'visible' : 'hidden'); - $$.axes.y2.append("text") - .attr("class", CLASS.axisY2Label) - .attr("transform", config.axis_rotated ? "" : "rotate(-90)") - .style("text-anchor", this.textAnchorForY2AxisLabel.bind(this)); - }; - Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { - var $$ = this.owner, config = $$.config, - axisParams = { - isCategory: $$.isCategorized(), - withOuterTick: withOuterTick, - tickMultiline: config.axis_x_tick_multiline, - tickWidth: config.axis_x_tick_width, - tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate, - withoutTransition: withoutTransition, - }, - axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient); - - if ($$.isTimeSeries() && tickValues && typeof tickValues !== "function") { - tickValues = tickValues.map(function (v) { return $$.parseDate(v); }); - } - - // Set tick - axis.tickFormat(tickFormat).tickValues(tickValues); - if ($$.isCategorized()) { - axis.tickCentered(config.axis_x_tick_centered); - if (isEmpty(config.axis_x_tick_culling)) { - config.axis_x_tick_culling = false; - } - } - - return axis; - }; - Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) { - var $$ = this.owner, config = $$.config, tickValues; - if (config.axis_x_tick_fit || config.axis_x_tick_count) { - tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries()); - } - if (axis) { - axis.tickValues(tickValues); - } else { - $$.xAxis.tickValues(tickValues); - $$.subXAxis.tickValues(tickValues); - } - return tickValues; - }; - Axis.prototype.getYAxis = function getYAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { - var $$ = this.owner, config = $$.config, - axisParams = { - withOuterTick: withOuterTick, - withoutTransition: withoutTransition, - tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate - }, - axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient).tickFormat(tickFormat); - if ($$.isTimeSeriesY()) { - axis.ticks($$.d3.time[config.axis_y_tick_time_value], config.axis_y_tick_time_interval); - } else { - axis.tickValues(tickValues); - } - return axis; - }; - Axis.prototype.getId = function getId(id) { - var config = this.owner.config; - return id in config.data_axes ? config.data_axes[id] : 'y'; - }; - Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() { - var $$ = this.owner, config = $$.config, - format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { return v < 0 ? v.toFixed(0) : v; }; - if (config.axis_x_tick_format) { - if (isFunction(config.axis_x_tick_format)) { - format = config.axis_x_tick_format; - } else if ($$.isTimeSeries()) { - format = function (date) { - return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : ""; - }; - } - } - return isFunction(format) ? function (v) { return format.call($$, v); } : format; - }; - Axis.prototype.getTickValues = function getTickValues(tickValues, axis) { - return tickValues ? tickValues : axis ? axis.tickValues() : undefined; - }; - Axis.prototype.getXAxisTickValues = function getXAxisTickValues() { - return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis); - }; - Axis.prototype.getYAxisTickValues = function getYAxisTickValues() { - return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis); - }; - Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() { - return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis); - }; - Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) { - var $$ = this.owner, config = $$.config, option; - if (axisId === 'y') { - option = config.axis_y_label; - } else if (axisId === 'y2') { - option = config.axis_y2_label; - } else if (axisId === 'x') { - option = config.axis_x_label; - } - return option; - }; - Axis.prototype.getLabelText = function getLabelText(axisId) { - var option = this.getLabelOptionByAxisId(axisId); - return isString(option) ? option : option ? option.text : null; - }; - Axis.prototype.setLabelText = function setLabelText(axisId, text) { - var $$ = this.owner, config = $$.config, - option = this.getLabelOptionByAxisId(axisId); - if (isString(option)) { - if (axisId === 'y') { - config.axis_y_label = text; - } else if (axisId === 'y2') { - config.axis_y2_label = text; - } else if (axisId === 'x') { - config.axis_x_label = text; - } - } else if (option) { - option.text = text; - } - }; - Axis.prototype.getLabelPosition = function getLabelPosition(axisId, defaultPosition) { - var option = this.getLabelOptionByAxisId(axisId), - position = (option && typeof option === 'object' && option.position) ? option.position : defaultPosition; - return { - isInner: position.indexOf('inner') >= 0, - isOuter: position.indexOf('outer') >= 0, - isLeft: position.indexOf('left') >= 0, - isCenter: position.indexOf('center') >= 0, - isRight: position.indexOf('right') >= 0, - isTop: position.indexOf('top') >= 0, - isMiddle: position.indexOf('middle') >= 0, - isBottom: position.indexOf('bottom') >= 0 - }; - }; - Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() { - return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right'); - }; - Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() { - return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); - }; - Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() { - return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); - }; - Axis.prototype.getLabelPositionById = function getLabelPositionById(id) { - return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition(); - }; - Axis.prototype.textForXAxisLabel = function textForXAxisLabel() { - return this.getLabelText('x'); - }; - Axis.prototype.textForYAxisLabel = function textForYAxisLabel() { - return this.getLabelText('y'); - }; - Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() { - return this.getLabelText('y2'); - }; - Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) { - var $$ = this.owner; - if (forHorizontal) { - return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width; - } else { - return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0; - } - }; - Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) { - if (forHorizontal) { - return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0"; - } else { - return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0"; - } - }; - Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) { - if (forHorizontal) { - return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end'; - } else { - return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end'; - } - }; - Axis.prototype.xForXAxisLabel = function xForXAxisLabel() { - return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); - }; - Axis.prototype.xForYAxisLabel = function xForYAxisLabel() { - return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); - }; - Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() { - return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); - }; - Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() { - return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); - }; - Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() { - return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); - }; - Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() { - return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); - }; - Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() { - var $$ = this.owner, config = $$.config, - position = this.getXAxisLabelPosition(); - if (config.axis_rotated) { - return position.isInner ? "1.2em" : -25 - this.getMaxTickWidth('x'); - } else { - return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em"; - } - }; - Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() { - var $$ = this.owner, - position = this.getYAxisLabelPosition(); - if ($$.config.axis_rotated) { - return position.isInner ? "-0.5em" : "3em"; - } else { - return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : (this.getMaxTickWidth('y') + 10)); - } - }; - Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() { - var $$ = this.owner, - position = this.getY2AxisLabelPosition(); - if ($$.config.axis_rotated) { - return position.isInner ? "1.2em" : "-2.2em"; - } else { - return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : (this.getMaxTickWidth('y2') + 15)); - } - }; - Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() { - var $$ = this.owner; - return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition()); - }; - Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() { - var $$ = this.owner; - return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition()); - }; - Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() { - var $$ = this.owner; - return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition()); - }; - Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) { - var $$ = this.owner, config = $$.config, - maxWidth = 0, targetsToShow, scale, axis, dummy, svg; - if (withoutRecompute && $$.currentMaxTickWidths[id]) { - return $$.currentMaxTickWidths[id]; - } - if ($$.svg) { - targetsToShow = $$.filterTargetsToShow($$.data.targets); - if (id === 'y') { - scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')); - axis = this.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, false, true, true); - } else if (id === 'y2') { - scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')); - axis = this.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, false, true, true); - } else { - scale = $$.x.copy().domain($$.getXDomain(targetsToShow)); - axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true); - this.updateXAxisTickValues(targetsToShow, axis); - } - dummy = $$.d3.select('body').append('div').classed('c3', true); - svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0), - svg.append('g').call(axis).each(function () { - $$.d3.select(this).selectAll('text').each(function () { - var box = this.getBoundingClientRect(); - if (maxWidth < box.width) { maxWidth = box.width; } - }); - dummy.remove(); - }); - } - $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth; - return $$.currentMaxTickWidths[id]; - }; - - Axis.prototype.updateLabels = function updateLabels(withTransition) { - var $$ = this.owner; - var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), - axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), - axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label); - (withTransition ? axisXLabel.transition() : axisXLabel) - .attr("x", this.xForXAxisLabel.bind(this)) - .attr("dx", this.dxForXAxisLabel.bind(this)) - .attr("dy", this.dyForXAxisLabel.bind(this)) - .text(this.textForXAxisLabel.bind(this)); - (withTransition ? axisYLabel.transition() : axisYLabel) - .attr("x", this.xForYAxisLabel.bind(this)) - .attr("dx", this.dxForYAxisLabel.bind(this)) - .attr("dy", this.dyForYAxisLabel.bind(this)) - .text(this.textForYAxisLabel.bind(this)); - (withTransition ? axisY2Label.transition() : axisY2Label) - .attr("x", this.xForY2AxisLabel.bind(this)) - .attr("dx", this.dxForY2AxisLabel.bind(this)) - .attr("dy", this.dyForY2AxisLabel.bind(this)) - .text(this.textForY2AxisLabel.bind(this)); - }; - Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) { - var p = typeof padding === 'number' ? padding : padding[key]; - if (!isValue(p)) { - return defaultValue; - } - if (padding.unit === 'ratio') { - return padding[key] * domainLength; - } - // assume padding is pixels if unit is not specified - return this.convertPixelsToAxisPadding(p, domainLength); - }; - Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) { - var $$ = this.owner, - length = $$.config.axis_rotated ? $$.width : $$.height; - return domainLength * (pixels / length); - }; - Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) { - var tickValues = values, targetCount, start, end, count, interval, i, tickValue; - if (tickCount) { - targetCount = isFunction(tickCount) ? tickCount() : tickCount; - // compute ticks according to tickCount - if (targetCount === 1) { - tickValues = [values[0]]; - } else if (targetCount === 2) { - tickValues = [values[0], values[values.length - 1]]; - } else if (targetCount > 2) { - count = targetCount - 2; - start = values[0]; - end = values[values.length - 1]; - interval = (end - start) / (count + 1); - // re-construct unique values - tickValues = [start]; - for (i = 0; i < count; i++) { - tickValue = +start + interval * (i + 1); - tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue); - } - tickValues.push(end); - } - } - if (!forTimeSeries) { tickValues = tickValues.sort(function (a, b) { return a - b; }); } - return tickValues; - }; - Axis.prototype.generateTransitions = function generateTransitions(duration) { - var $$ = this.owner, axes = $$.axes; - return { - axisX: duration ? axes.x.transition().duration(duration) : axes.x, - axisY: duration ? axes.y.transition().duration(duration) : axes.y, - axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2, - axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx - }; - }; - Axis.prototype.redraw = function redraw(transitions, isHidden) { - var $$ = this.owner; - $$.axes.x.style("opacity", isHidden ? 0 : 1); - $$.axes.y.style("opacity", isHidden ? 0 : 1); - $$.axes.y2.style("opacity", isHidden ? 0 : 1); - $$.axes.subx.style("opacity", isHidden ? 0 : 1); - transitions.axisX.call($$.xAxis); - transitions.axisY.call($$.yAxis); - transitions.axisY2.call($$.y2Axis); - transitions.axisSubX.call($$.subXAxis); - }; - - c3_chart_internal_fn.getClipPath = function (id) { - var isIE9 = window.navigator.appVersion.toLowerCase().indexOf("msie 9.") >= 0; - return "url(" + (isIE9 ? "" : document.URL.split('#')[0]) + "#" + id + ")"; - }; - c3_chart_internal_fn.appendClip = function (parent, id) { - return parent.append("clipPath").attr("id", id).append("rect"); - }; - c3_chart_internal_fn.getAxisClipX = function (forHorizontal) { - // axis line width + padding for left - var left = Math.max(30, this.margin.left); - return forHorizontal ? -(1 + left) : -(left - 1); - }; - c3_chart_internal_fn.getAxisClipY = function (forHorizontal) { - return forHorizontal ? -20 : -this.margin.top; - }; - c3_chart_internal_fn.getXAxisClipX = function () { - var $$ = this; - return $$.getAxisClipX(!$$.config.axis_rotated); - }; - c3_chart_internal_fn.getXAxisClipY = function () { - var $$ = this; - return $$.getAxisClipY(!$$.config.axis_rotated); - }; - c3_chart_internal_fn.getYAxisClipX = function () { - var $$ = this; - return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated); - }; - c3_chart_internal_fn.getYAxisClipY = function () { - var $$ = this; - return $$.getAxisClipY($$.config.axis_rotated); - }; - c3_chart_internal_fn.getAxisClipWidth = function (forHorizontal) { - var $$ = this, - left = Math.max(30, $$.margin.left), - right = Math.max(30, $$.margin.right); - // width + axis line width + padding for left/right - return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20; - }; - c3_chart_internal_fn.getAxisClipHeight = function (forHorizontal) { - // less than 20 is not enough to show the axis label 'outer' without legend - return (forHorizontal ? this.margin.bottom : (this.margin.top + this.height)) + 20; - }; - c3_chart_internal_fn.getXAxisClipWidth = function () { - var $$ = this; - return $$.getAxisClipWidth(!$$.config.axis_rotated); - }; - c3_chart_internal_fn.getXAxisClipHeight = function () { - var $$ = this; - return $$.getAxisClipHeight(!$$.config.axis_rotated); - }; - c3_chart_internal_fn.getYAxisClipWidth = function () { - var $$ = this; - return $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0); - }; - c3_chart_internal_fn.getYAxisClipHeight = function () { - var $$ = this; - return $$.getAxisClipHeight($$.config.axis_rotated); - }; - - c3_chart_internal_fn.initPie = function () { - var $$ = this, d3 = $$.d3, config = $$.config; - $$.pie = d3.layout.pie().value(function (d) { - return d.values.reduce(function (a, b) { return a + b.value; }, 0); - }); - if (!config.data_order) { - $$.pie.sort(null); - } - }; - - c3_chart_internal_fn.updateRadius = function () { - var $$ = this, config = $$.config, - w = config.gauge_width || config.donut_width; - $$.radiusExpanded = Math.min($$.arcWidth, $$.arcHeight) / 2; - $$.radius = $$.radiusExpanded * 0.95; - $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6; - $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0; - }; - - c3_chart_internal_fn.updateArc = function () { - var $$ = this; - $$.svgArc = $$.getSvgArc(); - $$.svgArcExpanded = $$.getSvgArcExpanded(); - $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98); - }; - - c3_chart_internal_fn.updateAngle = function (d) { - var $$ = this, config = $$.config, - found = false, index = 0, - gMin, gMax, gTic, gValue; - - if (!config) { - return null; - } - - $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) { - if (! found && t.data.id === d.data.id) { - found = true; - d = t; - d.index = index; - } - index++; - }); - if (isNaN(d.startAngle)) { - d.startAngle = 0; - } - if (isNaN(d.endAngle)) { - d.endAngle = d.startAngle; - } - if ($$.isGaugeType(d.data)) { - gMin = config.gauge_min; - gMax = config.gauge_max; - gTic = (Math.PI * (config.gauge_fullCircle ? 2 : 1)) / (gMax - gMin); - gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : (gMax - gMin); - d.startAngle = config.gauge_startingAngle; - d.endAngle = d.startAngle + gTic * gValue; - } - return found ? d : null; - }; - - c3_chart_internal_fn.getSvgArc = function () { - var $$ = this, - arc = $$.d3.svg.arc().outerRadius($$.radius).innerRadius($$.innerRadius), - newArc = function (d, withoutUpdate) { - var updated; - if (withoutUpdate) { return arc(d); } // for interpolate - updated = $$.updateAngle(d); - return updated ? arc(updated) : "M 0 0"; - }; - // TODO: extends all function - newArc.centroid = arc.centroid; - return newArc; - }; - - c3_chart_internal_fn.getSvgArcExpanded = function (rate) { - var $$ = this, - arc = $$.d3.svg.arc().outerRadius($$.radiusExpanded * (rate ? rate : 1)).innerRadius($$.innerRadius); - return function (d) { - var updated = $$.updateAngle(d); - return updated ? arc(updated) : "M 0 0"; - }; - }; - - c3_chart_internal_fn.getArc = function (d, withoutUpdate, force) { - return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : "M 0 0"; - }; - - - c3_chart_internal_fn.transformForArcLabel = function (d) { - var $$ = this, config = $$.config, - updated = $$.updateAngle(d), c, x, y, h, ratio, translate = ""; - if (updated && !$$.hasType('gauge')) { - c = this.svgArc.centroid(updated); - x = isNaN(c[0]) ? 0 : c[0]; - y = isNaN(c[1]) ? 0 : c[1]; - h = Math.sqrt(x * x + y * y); - if ($$.hasType('donut') && config.donut_label_ratio) { - ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio; - } else if ($$.hasType('pie') && config.pie_label_ratio) { - ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio; - } else { - ratio = $$.radius && h ? (36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius / h : 0; - } - translate = "translate(" + (x * ratio) + ',' + (y * ratio) + ")"; - } - return translate; - }; - - c3_chart_internal_fn.getArcRatio = function (d) { - var $$ = this, - config = $$.config, - whole = Math.PI * ($$.hasType('gauge') && !config.gauge_fullCircle ? 1 : 2); - return d ? (d.endAngle - d.startAngle) / whole : null; - }; - - c3_chart_internal_fn.convertToArcData = function (d) { - return this.addName({ - id: d.data.id, - value: d.value, - ratio: this.getArcRatio(d), - index: d.index - }); - }; - - c3_chart_internal_fn.textForArcLabel = function (d) { - var $$ = this, - updated, value, ratio, id, format; - if (! $$.shouldShowArcLabel()) { return ""; } - updated = $$.updateAngle(d); - value = updated ? updated.value : null; - ratio = $$.getArcRatio(updated); - id = d.data.id; - if (! $$.hasType('gauge') && ! $$.meetsArcLabelThreshold(ratio)) { return ""; } - format = $$.getArcLabelFormat(); - return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio); - }; - - c3_chart_internal_fn.expandArc = function (targetIds) { - var $$ = this, interval; - - // MEMO: avoid to cancel transition - if ($$.transiting) { - interval = window.setInterval(function () { - if (!$$.transiting) { - window.clearInterval(interval); - if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) { - $$.expandArc(targetIds); - } - } - }, 10); - return; - } - - targetIds = $$.mapToTargetIds(targetIds); - - $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) { - if (! $$.shouldExpand(d.data.id)) { return; } - $$.d3.select(this).selectAll('path') - .transition().duration($$.expandDuration(d.data.id)) - .attr("d", $$.svgArcExpanded) - .transition().duration($$.expandDuration(d.data.id) * 2) - .attr("d", $$.svgArcExpandedSub) - .each(function (d) { - if ($$.isDonutType(d.data)) { - // callback here - } - }); - }); - }; - - c3_chart_internal_fn.unexpandArc = function (targetIds) { - var $$ = this; - - if ($$.transiting) { return; } - - targetIds = $$.mapToTargetIds(targetIds); - - $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).selectAll('path') - .transition().duration(function(d) { - return $$.expandDuration(d.data.id); - }) - .attr("d", $$.svgArc); - $$.svg.selectAll('.' + CLASS.arc) - .style("opacity", 1); - }; - - c3_chart_internal_fn.expandDuration = function (id) { - var $$ = this, config = $$.config; - - if ($$.isDonutType(id)) { - return config.donut_expand_duration; - } else if ($$.isGaugeType(id)) { - return config.gauge_expand_duration; - } else if ($$.isPieType(id)) { - return config.pie_expand_duration; - } else { - return 50; - } - - }; - - c3_chart_internal_fn.shouldExpand = function (id) { - var $$ = this, config = $$.config; - return ($$.isDonutType(id) && config.donut_expand) || - ($$.isGaugeType(id) && config.gauge_expand) || - ($$.isPieType(id) && config.pie_expand); - }; - - c3_chart_internal_fn.shouldShowArcLabel = function () { - var $$ = this, config = $$.config, shouldShow = true; - if ($$.hasType('donut')) { - shouldShow = config.donut_label_show; - } else if ($$.hasType('pie')) { - shouldShow = config.pie_label_show; - } - // when gauge, always true - return shouldShow; - }; - - c3_chart_internal_fn.meetsArcLabelThreshold = function (ratio) { - var $$ = this, config = $$.config, - threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold; - return ratio >= threshold; - }; - - c3_chart_internal_fn.getArcLabelFormat = function () { - var $$ = this, config = $$.config, - format = config.pie_label_format; - if ($$.hasType('gauge')) { - format = config.gauge_label_format; - } else if ($$.hasType('donut')) { - format = config.donut_label_format; - } - return format; - }; - - c3_chart_internal_fn.getArcTitle = function () { - var $$ = this; - return $$.hasType('donut') ? $$.config.donut_title : ""; - }; - - c3_chart_internal_fn.updateTargetsForArc = function (targets) { - var $$ = this, main = $$.main, - mainPieUpdate, mainPieEnter, - classChartArc = $$.classChartArc.bind($$), - classArcs = $$.classArcs.bind($$), - classFocus = $$.classFocus.bind($$); - mainPieUpdate = main.select('.' + CLASS.chartArcs).selectAll('.' + CLASS.chartArc) - .data($$.pie(targets)) - .attr("class", function (d) { return classChartArc(d) + classFocus(d.data); }); - mainPieEnter = mainPieUpdate.enter().append("g") - .attr("class", classChartArc); - mainPieEnter.append('g') - .attr('class', classArcs); - mainPieEnter.append("text") - .attr("dy", $$.hasType('gauge') ? "-.1em" : ".35em") - .style("opacity", 0) - .style("text-anchor", "middle") - .style("pointer-events", "none"); - // MEMO: can not keep same color..., but not bad to update color in redraw - //mainPieUpdate.exit().remove(); - }; - - c3_chart_internal_fn.initArc = function () { - var $$ = this; - $$.arcs = $$.main.select('.' + CLASS.chart).append("g") - .attr("class", CLASS.chartArcs) - .attr("transform", $$.getTranslate('arc')); - $$.arcs.append('text') - .attr('class', CLASS.chartArcsTitle) - .style("text-anchor", "middle") - .text($$.getArcTitle()); - }; - - c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransform) { - var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main, - mainArc; - mainArc = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arc) - .data($$.arcData.bind($$)); - mainArc.enter().append('path') - .attr("class", $$.classArc.bind($$)) - .style("fill", function (d) { return $$.color(d.data); }) - .style("cursor", function (d) { return config.interaction_enabled && config.data_selection_isselectable(d) ? "pointer" : null; }) - .style("opacity", 0) - .each(function (d) { - if ($$.isGaugeType(d.data)) { - d.startAngle = d.endAngle = config.gauge_startingAngle; - } - this._current = d; - }); - mainArc - .attr("transform", function (d) { return !$$.isGaugeType(d.data) && withTransform ? "scale(0)" : ""; }) - .style("opacity", function (d) { return d === this._current ? 0 : 1; }) - .on('mouseover', config.interaction_enabled ? function (d) { - var updated, arcData; - if ($$.transiting) { // skip while transiting - return; - } - updated = $$.updateAngle(d); - if (updated) { - arcData = $$.convertToArcData(updated); - // transitions - $$.expandArc(updated.data.id); - $$.api.focus(updated.data.id); - $$.toggleFocusLegend(updated.data.id, true); - $$.config.data_onmouseover(arcData, this); - } - } : null) - .on('mousemove', config.interaction_enabled ? function (d) { - var updated = $$.updateAngle(d), arcData, selectedData; - if (updated) { - arcData = $$.convertToArcData(updated), - selectedData = [arcData]; - $$.showTooltip(selectedData, this); - } - } : null) - .on('mouseout', config.interaction_enabled ? function (d) { - var updated, arcData; - if ($$.transiting) { // skip while transiting - return; - } - updated = $$.updateAngle(d); - if (updated) { - arcData = $$.convertToArcData(updated); - // transitions - $$.unexpandArc(updated.data.id); - $$.api.revert(); - $$.revertLegend(); - $$.hideTooltip(); - $$.config.data_onmouseout(arcData, this); - } - } : null) - .on('click', config.interaction_enabled ? function (d, i) { - var updated = $$.updateAngle(d), arcData; - if (updated) { - arcData = $$.convertToArcData(updated); - if ($$.toggleShape) { - $$.toggleShape(this, arcData, i); - } - $$.config.data_onclick.call($$.api, arcData, this); - } - } : null) - .each(function () { $$.transiting = true; }) - .transition().duration(duration) - .attrTween("d", function (d) { - var updated = $$.updateAngle(d), interpolate; - if (! updated) { - return function () { return "M 0 0"; }; - } - // if (this._current === d) { - // this._current = { - // startAngle: Math.PI*2, - // endAngle: Math.PI*2, - // }; - // } - if (isNaN(this._current.startAngle)) { - this._current.startAngle = 0; - } - if (isNaN(this._current.endAngle)) { - this._current.endAngle = this._current.startAngle; - } - interpolate = d3.interpolate(this._current, updated); - this._current = interpolate(0); - return function (t) { - var interpolated = interpolate(t); - interpolated.data = d.data; // data.id will be updated by interporator - return $$.getArc(interpolated, true); - }; - }) - .attr("transform", withTransform ? "scale(1)" : "") - .style("fill", function (d) { - return $$.levelColor ? $$.levelColor(d.data.values[0].value) : $$.color(d.data.id); - }) // Where gauge reading color would receive customization. - .style("opacity", 1) - .call($$.endall, function () { - $$.transiting = false; - }); - mainArc.exit().transition().duration(durationForExit) - .style('opacity', 0) - .remove(); - main.selectAll('.' + CLASS.chartArc).select('text') - .style("opacity", 0) - .attr('class', function (d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : ''; }) - .text($$.textForArcLabel.bind($$)) - .attr("transform", $$.transformForArcLabel.bind($$)) - .style('font-size', function (d) { return $$.isGaugeType(d.data) ? Math.round($$.radius / 5) + 'px' : ''; }) - .transition().duration(duration) - .style("opacity", function (d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0; }); - main.select('.' + CLASS.chartArcsTitle) - .style("opacity", $$.hasType('donut') || $$.hasType('gauge') ? 1 : 0); - - if ($$.hasType('gauge')) { - $$.arcs.select('.' + CLASS.chartArcsBackground) - .attr("d", function () { - var d = { - data: [{value: config.gauge_max}], - startAngle: config.gauge_startingAngle, - endAngle: -1 * config.gauge_startingAngle - }; - return $$.getArc(d, true, true); - }); - $$.arcs.select('.' + CLASS.chartArcsGaugeUnit) - .attr("dy", ".75em") - .text(config.gauge_label_show ? config.gauge_units : ''); - $$.arcs.select('.' + CLASS.chartArcsGaugeMin) - .attr("dx", -1 * ($$.innerRadius + (($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2))) + "px") - .attr("dy", "1.2em") - .text(config.gauge_label_show ? config.gauge_min : ''); - $$.arcs.select('.' + CLASS.chartArcsGaugeMax) - .attr("dx", $$.innerRadius + (($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + "px") - .attr("dy", "1.2em") - .text(config.gauge_label_show ? config.gauge_max : ''); - } - }; - c3_chart_internal_fn.initGauge = function () { - var arcs = this.arcs; - if (this.hasType('gauge')) { - arcs.append('path') - .attr("class", CLASS.chartArcsBackground); - arcs.append("text") - .attr("class", CLASS.chartArcsGaugeUnit) - .style("text-anchor", "middle") - .style("pointer-events", "none"); - arcs.append("text") - .attr("class", CLASS.chartArcsGaugeMin) - .style("text-anchor", "middle") - .style("pointer-events", "none"); - arcs.append("text") - .attr("class", CLASS.chartArcsGaugeMax) - .style("text-anchor", "middle") - .style("pointer-events", "none"); - } - }; - c3_chart_internal_fn.getGaugeLabelHeight = function () { - return this.config.gauge_label_show ? 20 : 0; - }; - - c3_chart_internal_fn.initRegion = function () { - var $$ = this; - $$.region = $$.main.append('g') - .attr("clip-path", $$.clipPath) - .attr("class", CLASS.regions); - }; - c3_chart_internal_fn.updateRegion = function (duration) { - var $$ = this, config = $$.config; - - // hide if arc type - $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); - - $$.mainRegion = $$.main.select('.' + CLASS.regions).selectAll('.' + CLASS.region) - .data(config.regions); - $$.mainRegion.enter().append('g') - .append('rect') - .style("fill-opacity", 0); - $$.mainRegion - .attr('class', $$.classRegion.bind($$)); - $$.mainRegion.exit().transition().duration(duration) - .style("opacity", 0) - .remove(); - }; - c3_chart_internal_fn.redrawRegion = function (withTransition) { - var $$ = this, - regions = $$.mainRegion.selectAll('rect').each(function () { - // data is binded to g and it's not transferred to rect (child node) automatically, - // then data of each rect has to be updated manually. - // TODO: there should be more efficient way to solve this? - var parentData = $$.d3.select(this.parentNode).datum(); - $$.d3.select(this).datum(parentData); - }), - x = $$.regionX.bind($$), - y = $$.regionY.bind($$), - w = $$.regionWidth.bind($$), - h = $$.regionHeight.bind($$); - return [ - (withTransition ? regions.transition() : regions) - .attr("x", x) - .attr("y", y) - .attr("width", w) - .attr("height", h) - .style("fill-opacity", function (d) { return isValue(d.opacity) ? d.opacity : 0.1; }) - ]; - }; - c3_chart_internal_fn.regionX = function (d) { - var $$ = this, config = $$.config, - xPos, yScale = d.axis === 'y' ? $$.y : $$.y2; - if (d.axis === 'y' || d.axis === 'y2') { - xPos = config.axis_rotated ? ('start' in d ? yScale(d.start) : 0) : 0; - } else { - xPos = config.axis_rotated ? 0 : ('start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0); - } - return xPos; - }; - c3_chart_internal_fn.regionY = function (d) { - var $$ = this, config = $$.config, - yPos, yScale = d.axis === 'y' ? $$.y : $$.y2; - if (d.axis === 'y' || d.axis === 'y2') { - yPos = config.axis_rotated ? 0 : ('end' in d ? yScale(d.end) : 0); - } else { - yPos = config.axis_rotated ? ('start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0) : 0; - } - return yPos; - }; - c3_chart_internal_fn.regionWidth = function (d) { - var $$ = this, config = $$.config, - start = $$.regionX(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; - if (d.axis === 'y' || d.axis === 'y2') { - end = config.axis_rotated ? ('end' in d ? yScale(d.end) : $$.width) : $$.width; - } else { - end = config.axis_rotated ? $$.width : ('end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width); - } - return end < start ? 0 : end - start; - }; - c3_chart_internal_fn.regionHeight = function (d) { - var $$ = this, config = $$.config, - start = this.regionY(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; - if (d.axis === 'y' || d.axis === 'y2') { - end = config.axis_rotated ? $$.height : ('start' in d ? yScale(d.start) : $$.height); - } else { - end = config.axis_rotated ? ('end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height) : $$.height; - } - return end < start ? 0 : end - start; - }; - c3_chart_internal_fn.isRegionOnX = function (d) { - return !d.axis || d.axis === 'x'; - }; - - c3_chart_internal_fn.drag = function (mouse) { - var $$ = this, config = $$.config, main = $$.main, d3 = $$.d3; - var sx, sy, mx, my, minX, maxX, minY, maxY; - - if ($$.hasArcType()) { return; } - if (! config.data_selection_enabled) { return; } // do nothing if not selectable - if (config.zoom_enabled && ! $$.zoom.altDomain) { return; } // skip if zoomable because of conflict drag dehavior - if (!config.data_selection_multiple) { return; } // skip when single selection because drag is used for multiple selection - - sx = $$.dragStart[0]; - sy = $$.dragStart[1]; - mx = mouse[0]; - my = mouse[1]; - minX = Math.min(sx, mx); - maxX = Math.max(sx, mx); - minY = (config.data_selection_grouped) ? $$.margin.top : Math.min(sy, my); - maxY = (config.data_selection_grouped) ? $$.height : Math.max(sy, my); - - main.select('.' + CLASS.dragarea) - .attr('x', minX) - .attr('y', minY) - .attr('width', maxX - minX) - .attr('height', maxY - minY); - // TODO: binary search when multiple xs - main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape) - .filter(function (d) { return config.data_selection_isselectable(d); }) - .each(function (d, i) { - var shape = d3.select(this), - isSelected = shape.classed(CLASS.SELECTED), - isIncluded = shape.classed(CLASS.INCLUDED), - _x, _y, _w, _h, toggle, isWithin = false, box; - if (shape.classed(CLASS.circle)) { - _x = shape.attr("cx") * 1; - _y = shape.attr("cy") * 1; - toggle = $$.togglePoint; - isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY; - } - else if (shape.classed(CLASS.bar)) { - box = getPathBox(this); - _x = box.x; - _y = box.y; - _w = box.width; - _h = box.height; - toggle = $$.togglePath; - isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY); - } else { - // line/area selection not supported yet - return; - } - if (isWithin ^ isIncluded) { - shape.classed(CLASS.INCLUDED, !isIncluded); - // TODO: included/unincluded callback here - shape.classed(CLASS.SELECTED, !isSelected); - toggle.call($$, !isSelected, shape, d, i); - } - }); - }; - - c3_chart_internal_fn.dragstart = function (mouse) { - var $$ = this, config = $$.config; - if ($$.hasArcType()) { return; } - if (! config.data_selection_enabled) { return; } // do nothing if not selectable - $$.dragStart = mouse; - $$.main.select('.' + CLASS.chart).append('rect') - .attr('class', CLASS.dragarea) - .style('opacity', 0.1); - $$.dragging = true; - }; - - c3_chart_internal_fn.dragend = function () { - var $$ = this, config = $$.config; - if ($$.hasArcType()) { return; } - if (! config.data_selection_enabled) { return; } // do nothing if not selectable - $$.main.select('.' + CLASS.dragarea) - .transition().duration(100) - .style('opacity', 0) - .remove(); - $$.main.selectAll('.' + CLASS.shape) - .classed(CLASS.INCLUDED, false); - $$.dragging = false; - }; - - c3_chart_internal_fn.selectPoint = function (target, d, i) { - var $$ = this, config = $$.config, - cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$), - cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$), - r = $$.pointSelectR.bind($$); - config.data_onselected.call($$.api, d, target.node()); - // add selected-circle on low layer g - $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i) - .data([d]) - .enter().append('circle') - .attr("class", function () { return $$.generateClass(CLASS.selectedCircle, i); }) - .attr("cx", cx) - .attr("cy", cy) - .attr("stroke", function () { return $$.color(d); }) - .attr("r", function (d) { return $$.pointSelectR(d) * 1.4; }) - .transition().duration(100) - .attr("r", r); - }; - c3_chart_internal_fn.unselectPoint = function (target, d, i) { - var $$ = this; - $$.config.data_onunselected.call($$.api, d, target.node()); - // remove selected-circle from low layer g - $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i) - .transition().duration(100).attr('r', 0) - .remove(); - }; - c3_chart_internal_fn.togglePoint = function (selected, target, d, i) { - selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i); - }; - c3_chart_internal_fn.selectPath = function (target, d) { - var $$ = this; - $$.config.data_onselected.call($$, d, target.node()); - if ($$.config.interaction_brighten) { - target.transition().duration(100) - .style("fill", function () { return $$.d3.rgb($$.color(d)).brighter(0.75); }); - } - }; - c3_chart_internal_fn.unselectPath = function (target, d) { - var $$ = this; - $$.config.data_onunselected.call($$, d, target.node()); - if ($$.config.interaction_brighten) { - target.transition().duration(100) - .style("fill", function () { return $$.color(d); }); - } - }; - c3_chart_internal_fn.togglePath = function (selected, target, d, i) { - selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i); - }; - c3_chart_internal_fn.getToggle = function (that, d) { - var $$ = this, toggle; - if (that.nodeName === 'circle') { - if ($$.isStepType(d)) { - // circle is hidden in step chart, so treat as within the click area - toggle = function () {}; // TODO: how to select step chart? - } else { - toggle = $$.togglePoint; - } - } - else if (that.nodeName === 'path') { - toggle = $$.togglePath; - } - return toggle; - }; - c3_chart_internal_fn.toggleShape = function (that, d, i) { - var $$ = this, d3 = $$.d3, config = $$.config, - shape = d3.select(that), isSelected = shape.classed(CLASS.SELECTED), - toggle = $$.getToggle(that, d).bind($$); - - if (config.data_selection_enabled && config.data_selection_isselectable(d)) { - if (!config.data_selection_multiple) { - $$.main.selectAll('.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : "")).selectAll('.' + CLASS.shape).each(function (d, i) { - var shape = d3.select(this); - if (shape.classed(CLASS.SELECTED)) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } - }); - } - shape.classed(CLASS.SELECTED, !isSelected); - toggle(!isSelected, shape, d, i); - } - }; - - c3_chart_internal_fn.initBrush = function () { - var $$ = this, d3 = $$.d3; - $$.brush = d3.svg.brush().on("brush", function () { $$.redrawForBrush(); }); - $$.brush.update = function () { - if ($$.context) { $$.context.select('.' + CLASS.brush).call(this); } - return this; - }; - $$.brush.scale = function (scale) { - return $$.config.axis_rotated ? this.y(scale) : this.x(scale); - }; - }; - c3_chart_internal_fn.initSubchart = function () { - var $$ = this, config = $$.config, - context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')), - visibility = config.subchart_show ? 'visible' : 'hidden'; - - context.style('visibility', visibility); - - // Define g for chart area - context.append('g') - .attr("clip-path", $$.clipPathForSubchart) - .attr('class', CLASS.chart); - - // Define g for bar chart area - context.select('.' + CLASS.chart).append("g") - .attr("class", CLASS.chartBars); - - // Define g for line chart area - context.select('.' + CLASS.chart).append("g") - .attr("class", CLASS.chartLines); - - // Add extent rect for Brush - context.append("g") - .attr("clip-path", $$.clipPath) - .attr("class", CLASS.brush) - .call($$.brush); - - // ATTENTION: This must be called AFTER chart added - // Add Axis - $$.axes.subx = context.append("g") - .attr("class", CLASS.axisX) - .attr("transform", $$.getTranslate('subx')) - .attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis) - .style("visibility", config.subchart_axis_x_show ? visibility : 'hidden'); - }; - c3_chart_internal_fn.updateTargetsForSubchart = function (targets) { - var $$ = this, context = $$.context, config = $$.config, - contextLineEnter, contextLineUpdate, contextBarEnter, contextBarUpdate, - classChartBar = $$.classChartBar.bind($$), - classBars = $$.classBars.bind($$), - classChartLine = $$.classChartLine.bind($$), - classLines = $$.classLines.bind($$), - classAreas = $$.classAreas.bind($$); - - if (config.subchart_show) { - //-- Bar --// - contextBarUpdate = context.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar) - .data(targets) - .attr('class', classChartBar); - contextBarEnter = contextBarUpdate.enter().append('g') - .style('opacity', 0) - .attr('class', classChartBar); - // Bars for each data - contextBarEnter.append('g') - .attr("class", classBars); - - //-- Line --// - contextLineUpdate = context.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine) - .data(targets) - .attr('class', classChartLine); - contextLineEnter = contextLineUpdate.enter().append('g') - .style('opacity', 0) - .attr('class', classChartLine); - // Lines for each data - contextLineEnter.append("g") - .attr("class", classLines); - // Area - contextLineEnter.append("g") - .attr("class", classAreas); - - //-- Brush --// - context.selectAll('.' + CLASS.brush + ' rect') - .attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2); - } - }; - c3_chart_internal_fn.updateBarForSubchart = function (durationForExit) { - var $$ = this; - $$.contextBar = $$.context.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar) - .data($$.barData.bind($$)); - $$.contextBar.enter().append('path') - .attr("class", $$.classBar.bind($$)) - .style("stroke", 'none') - .style("fill", $$.color); - $$.contextBar - .style("opacity", $$.initialOpacity.bind($$)); - $$.contextBar.exit().transition().duration(durationForExit) - .style('opacity', 0) - .remove(); - }; - c3_chart_internal_fn.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) { - (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar) - .attr('d', drawBarOnSub) - .style('opacity', 1); - }; - c3_chart_internal_fn.updateLineForSubchart = function (durationForExit) { - var $$ = this; - $$.contextLine = $$.context.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line) - .data($$.lineData.bind($$)); - $$.contextLine.enter().append('path') - .attr('class', $$.classLine.bind($$)) - .style('stroke', $$.color); - $$.contextLine - .style("opacity", $$.initialOpacity.bind($$)); - $$.contextLine.exit().transition().duration(durationForExit) - .style('opacity', 0) - .remove(); - }; - c3_chart_internal_fn.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) { - (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine) - .attr("d", drawLineOnSub) - .style('opacity', 1); - }; - c3_chart_internal_fn.updateAreaForSubchart = function (durationForExit) { - var $$ = this, d3 = $$.d3; - $$.contextArea = $$.context.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area) - .data($$.lineData.bind($$)); - $$.contextArea.enter().append('path') - .attr("class", $$.classArea.bind($$)) - .style("fill", $$.color) - .style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; }); - $$.contextArea - .style("opacity", 0); - $$.contextArea.exit().transition().duration(durationForExit) - .style('opacity', 0) - .remove(); - }; - c3_chart_internal_fn.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) { - (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea) - .attr("d", drawAreaOnSub) - .style("fill", this.color) - .style("opacity", this.orgAreaOpacity); - }; - c3_chart_internal_fn.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) { - var $$ = this, d3 = $$.d3, config = $$.config, - drawAreaOnSub, drawBarOnSub, drawLineOnSub; - - $$.context.style('visibility', config.subchart_show ? 'visible' : 'hidden'); - - // subchart - if (config.subchart_show) { - // reflect main chart to extent on subchart if zoomed - if (d3.event && d3.event.type === 'zoom') { - $$.brush.extent($$.x.orgDomain()).update(); - } - // update subchart elements if needed - if (withSubchart) { - - // extent rect - if (!$$.brush.empty()) { - $$.brush.extent($$.x.orgDomain()).update(); - } - // setup drawer - MEMO: this must be called after axis updated - drawAreaOnSub = $$.generateDrawArea(areaIndices, true); - drawBarOnSub = $$.generateDrawBar(barIndices, true); - drawLineOnSub = $$.generateDrawLine(lineIndices, true); - - $$.updateBarForSubchart(duration); - $$.updateLineForSubchart(duration); - $$.updateAreaForSubchart(duration); - - $$.redrawBarForSubchart(drawBarOnSub, duration, duration); - $$.redrawLineForSubchart(drawLineOnSub, duration, duration); - $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration); - } - } - }; - c3_chart_internal_fn.redrawForBrush = function () { - var $$ = this, x = $$.x; - $$.redraw({ - withTransition: false, - withY: $$.config.zoom_rescale, - withSubchart: false, - withUpdateXDomain: true, - withDimension: false - }); - $$.config.subchart_onbrush.call($$.api, x.orgDomain()); - }; - c3_chart_internal_fn.transformContext = function (withTransition, transitions) { - var $$ = this, subXAxis; - if (transitions && transitions.axisSubX) { - subXAxis = transitions.axisSubX; - } else { - subXAxis = $$.context.select('.' + CLASS.axisX); - if (withTransition) { subXAxis = subXAxis.transition(); } - } - $$.context.attr("transform", $$.getTranslate('context')); - subXAxis.attr("transform", $$.getTranslate('subx')); - }; - c3_chart_internal_fn.getDefaultExtent = function () { - var $$ = this, config = $$.config, - extent = isFunction(config.axis_x_extent) ? config.axis_x_extent($$.getXDomain($$.data.targets)) : config.axis_x_extent; - if ($$.isTimeSeries()) { - extent = [$$.parseDate(extent[0]), $$.parseDate(extent[1])]; - } - return extent; - }; - - c3_chart_internal_fn.initZoom = function () { - var $$ = this, d3 = $$.d3, config = $$.config, startEvent; - - $$.zoom = d3.behavior.zoom() - .on("zoomstart", function () { - startEvent = d3.event.sourceEvent; - $$.zoom.altDomain = d3.event.sourceEvent.altKey ? $$.x.orgDomain() : null; - config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent); - }) - .on("zoom", function () { - $$.redrawForZoom.call($$); - }) - .on('zoomend', function () { - var event = d3.event.sourceEvent; - // if click, do nothing. otherwise, click interaction will be canceled. - if (event && startEvent.clientX === event.clientX && startEvent.clientY === event.clientY) { - return; - } - $$.redrawEventRect(); - $$.updateZoom(); - config.zoom_onzoomend.call($$.api, $$.x.orgDomain()); - }); - $$.zoom.scale = function (scale) { - return config.axis_rotated ? this.y(scale) : this.x(scale); - }; - $$.zoom.orgScaleExtent = function () { - var extent = config.zoom_extent ? config.zoom_extent : [1, 10]; - return [extent[0], Math.max($$.getMaxDataCount() / extent[1], extent[1])]; - }; - $$.zoom.updateScaleExtent = function () { - var ratio = diffDomain($$.x.orgDomain()) / diffDomain($$.getZoomDomain()), - extent = this.orgScaleExtent(); - this.scaleExtent([extent[0] * ratio, extent[1] * ratio]); - return this; - }; - }; - c3_chart_internal_fn.getZoomDomain = function () { - var $$ = this, config = $$.config, d3 = $$.d3, - min = d3.min([$$.orgXDomain[0], config.zoom_x_min]), - max = d3.max([$$.orgXDomain[1], config.zoom_x_max]); - return [min, max]; - }; - c3_chart_internal_fn.updateZoom = function () { - var $$ = this, z = $$.config.zoom_enabled ? $$.zoom : function () {}; - $$.main.select('.' + CLASS.zoomRect).call(z).on("dblclick.zoom", null); - $$.main.selectAll('.' + CLASS.eventRect).call(z).on("dblclick.zoom", null); - }; - c3_chart_internal_fn.redrawForZoom = function () { - var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x; - if (!config.zoom_enabled) { - return; - } - if ($$.filterTargetsToShow($$.data.targets).length === 0) { - return; - } - if (d3.event.sourceEvent.type === 'mousemove' && zoom.altDomain) { - x.domain(zoom.altDomain); - zoom.scale(x).updateScaleExtent(); - return; - } - if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { - x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]); - } - $$.redraw({ - withTransition: false, - withY: config.zoom_rescale, - withSubchart: false, - withEventRect: false, - withDimension: false - }); - if (d3.event.sourceEvent.type === 'mousemove') { - $$.cancelClick = true; - } - config.zoom_onzoom.call($$.api, x.orgDomain()); - }; - - c3_chart_internal_fn.generateColor = function () { - var $$ = this, config = $$.config, d3 = $$.d3, - colors = config.data_colors, - pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.scale.category10().range(), - callback = config.data_color, - ids = []; - - return function (d) { - var id = d.id || (d.data && d.data.id) || d, color; - - // if callback function is provided - if (colors[id] instanceof Function) { - color = colors[id](d); - } - // if specified, choose that color - else if (colors[id]) { - color = colors[id]; - } - // if not specified, choose from pattern - else { - if (ids.indexOf(id) < 0) { ids.push(id); } - color = pattern[ids.indexOf(id) % pattern.length]; - colors[id] = color; - } - return callback instanceof Function ? callback(color, d) : color; - }; - }; - c3_chart_internal_fn.generateLevelColor = function () { - var $$ = this, config = $$.config, - colors = config.color_pattern, - threshold = config.color_threshold, - asValue = threshold.unit === 'value', - values = threshold.values && threshold.values.length ? threshold.values : [], - max = threshold.max || 100; - return notEmpty(config.color_threshold) ? function (value) { - var i, v, color = colors[colors.length - 1]; - for (i = 0; i < values.length; i++) { - v = asValue ? value : (value * 100 / max); - if (v < values[i]) { - color = colors[i]; - break; - } - } - return color; - } : null; - }; - - c3_chart_internal_fn.getYFormat = function (forArc) { - var $$ = this, - formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat, - formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format; - return function (v, ratio, id) { - var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY; - return format.call($$, v, ratio); - }; - }; - c3_chart_internal_fn.yFormat = function (v) { - var $$ = this, config = $$.config, - format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat; - return format(v); - }; - c3_chart_internal_fn.y2Format = function (v) { - var $$ = this, config = $$.config, - format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat; - return format(v); - }; - c3_chart_internal_fn.defaultValueFormat = function (v) { - return isValue(v) ? +v : ""; - }; - c3_chart_internal_fn.defaultArcValueFormat = function (v, ratio) { - return (ratio * 100).toFixed(1) + '%'; - }; - c3_chart_internal_fn.dataLabelFormat = function (targetId) { - var $$ = this, data_labels = $$.config.data_labels, - format, defaultFormat = function (v) { return isValue(v) ? +v : ""; }; - // find format according to axis id - if (typeof data_labels.format === 'function') { - format = data_labels.format; - } else if (typeof data_labels.format === 'object') { - if (data_labels.format[targetId]) { - format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId]; - } else { - format = function () { return ''; }; - } - } else { - format = defaultFormat; - } - return format; - }; - - c3_chart_internal_fn.hasCaches = function (ids) { - for (var i = 0; i < ids.length; i++) { - if (! (ids[i] in this.cache)) { return false; } - } - return true; - }; - c3_chart_internal_fn.addCache = function (id, target) { - this.cache[id] = this.cloneTarget(target); - }; - c3_chart_internal_fn.getCaches = function (ids) { - var targets = [], i; - for (i = 0; i < ids.length; i++) { - if (ids[i] in this.cache) { targets.push(this.cloneTarget(this.cache[ids[i]])); } - } - return targets; - }; - - var CLASS = c3_chart_internal_fn.CLASS = { - target: 'c3-target', - chart: 'c3-chart', - chartLine: 'c3-chart-line', - chartLines: 'c3-chart-lines', - chartBar: 'c3-chart-bar', - chartBars: 'c3-chart-bars', - chartText: 'c3-chart-text', - chartTexts: 'c3-chart-texts', - chartArc: 'c3-chart-arc', - chartArcs: 'c3-chart-arcs', - chartArcsTitle: 'c3-chart-arcs-title', - chartArcsBackground: 'c3-chart-arcs-background', - chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit', - chartArcsGaugeMax: 'c3-chart-arcs-gauge-max', - chartArcsGaugeMin: 'c3-chart-arcs-gauge-min', - selectedCircle: 'c3-selected-circle', - selectedCircles: 'c3-selected-circles', - eventRect: 'c3-event-rect', - eventRects: 'c3-event-rects', - eventRectsSingle: 'c3-event-rects-single', - eventRectsMultiple: 'c3-event-rects-multiple', - zoomRect: 'c3-zoom-rect', - brush: 'c3-brush', - focused: 'c3-focused', - defocused: 'c3-defocused', - region: 'c3-region', - regions: 'c3-regions', - title: 'c3-title', - tooltipContainer: 'c3-tooltip-container', - tooltip: 'c3-tooltip', - tooltipName: 'c3-tooltip-name', - shape: 'c3-shape', - shapes: 'c3-shapes', - line: 'c3-line', - lines: 'c3-lines', - bar: 'c3-bar', - bars: 'c3-bars', - circle: 'c3-circle', - circles: 'c3-circles', - arc: 'c3-arc', - arcs: 'c3-arcs', - area: 'c3-area', - areas: 'c3-areas', - empty: 'c3-empty', - text: 'c3-text', - texts: 'c3-texts', - gaugeValue: 'c3-gauge-value', - grid: 'c3-grid', - gridLines: 'c3-grid-lines', - xgrid: 'c3-xgrid', - xgrids: 'c3-xgrids', - xgridLine: 'c3-xgrid-line', - xgridLines: 'c3-xgrid-lines', - xgridFocus: 'c3-xgrid-focus', - ygrid: 'c3-ygrid', - ygrids: 'c3-ygrids', - ygridLine: 'c3-ygrid-line', - ygridLines: 'c3-ygrid-lines', - axis: 'c3-axis', - axisX: 'c3-axis-x', - axisXLabel: 'c3-axis-x-label', - axisY: 'c3-axis-y', - axisYLabel: 'c3-axis-y-label', - axisY2: 'c3-axis-y2', - axisY2Label: 'c3-axis-y2-label', - legendBackground: 'c3-legend-background', - legendItem: 'c3-legend-item', - legendItemEvent: 'c3-legend-item-event', - legendItemTile: 'c3-legend-item-tile', - legendItemHidden: 'c3-legend-item-hidden', - legendItemFocused: 'c3-legend-item-focused', - dragarea: 'c3-dragarea', - EXPANDED: '_expanded_', - SELECTED: '_selected_', - INCLUDED: '_included_' - }; - c3_chart_internal_fn.generateClass = function (prefix, targetId) { - return " " + prefix + " " + prefix + this.getTargetSelectorSuffix(targetId); - }; - c3_chart_internal_fn.classText = function (d) { - return this.generateClass(CLASS.text, d.index); - }; - c3_chart_internal_fn.classTexts = function (d) { - return this.generateClass(CLASS.texts, d.id); - }; - c3_chart_internal_fn.classShape = function (d) { - return this.generateClass(CLASS.shape, d.index); - }; - c3_chart_internal_fn.classShapes = function (d) { - return this.generateClass(CLASS.shapes, d.id); - }; - c3_chart_internal_fn.classLine = function (d) { - return this.classShape(d) + this.generateClass(CLASS.line, d.id); - }; - c3_chart_internal_fn.classLines = function (d) { - return this.classShapes(d) + this.generateClass(CLASS.lines, d.id); - }; - c3_chart_internal_fn.classCircle = function (d) { - return this.classShape(d) + this.generateClass(CLASS.circle, d.index); - }; - c3_chart_internal_fn.classCircles = function (d) { - return this.classShapes(d) + this.generateClass(CLASS.circles, d.id); - }; - c3_chart_internal_fn.classBar = function (d) { - return this.classShape(d) + this.generateClass(CLASS.bar, d.index); - }; - c3_chart_internal_fn.classBars = function (d) { - return this.classShapes(d) + this.generateClass(CLASS.bars, d.id); - }; - c3_chart_internal_fn.classArc = function (d) { - return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id); - }; - c3_chart_internal_fn.classArcs = function (d) { - return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id); - }; - c3_chart_internal_fn.classArea = function (d) { - return this.classShape(d) + this.generateClass(CLASS.area, d.id); - }; - c3_chart_internal_fn.classAreas = function (d) { - return this.classShapes(d) + this.generateClass(CLASS.areas, d.id); - }; - c3_chart_internal_fn.classRegion = function (d, i) { - return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : ''); - }; - c3_chart_internal_fn.classEvent = function (d) { - return this.generateClass(CLASS.eventRect, d.index); - }; - c3_chart_internal_fn.classTarget = function (id) { - var $$ = this; - var additionalClassSuffix = $$.config.data_classes[id], additionalClass = ''; - if (additionalClassSuffix) { - additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix; - } - return $$.generateClass(CLASS.target, id) + additionalClass; - }; - c3_chart_internal_fn.classFocus = function (d) { - return this.classFocused(d) + this.classDefocused(d); - }; - c3_chart_internal_fn.classFocused = function (d) { - return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : ''); - }; - c3_chart_internal_fn.classDefocused = function (d) { - return ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : ''); - }; - c3_chart_internal_fn.classChartText = function (d) { - return CLASS.chartText + this.classTarget(d.id); - }; - c3_chart_internal_fn.classChartLine = function (d) { - return CLASS.chartLine + this.classTarget(d.id); - }; - c3_chart_internal_fn.classChartBar = function (d) { - return CLASS.chartBar + this.classTarget(d.id); - }; - c3_chart_internal_fn.classChartArc = function (d) { - return CLASS.chartArc + this.classTarget(d.data.id); - }; - c3_chart_internal_fn.getTargetSelectorSuffix = function (targetId) { - return targetId || targetId === 0 ? ('-' + targetId).replace(/[\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g, '-') : ''; - }; - c3_chart_internal_fn.selectorTarget = function (id, prefix) { - return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id); - }; - c3_chart_internal_fn.selectorTargets = function (ids, prefix) { - var $$ = this; - ids = ids || []; - return ids.length ? ids.map(function (id) { return $$.selectorTarget(id, prefix); }) : null; - }; - c3_chart_internal_fn.selectorLegend = function (id) { - return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id); - }; - c3_chart_internal_fn.selectorLegends = function (ids) { - var $$ = this; - return ids && ids.length ? ids.map(function (id) { return $$.selectorLegend(id); }) : null; - }; - - var isValue = c3_chart_internal_fn.isValue = function (v) { - return v || v === 0; - }, - isFunction = c3_chart_internal_fn.isFunction = function (o) { - return typeof o === 'function'; - }, - isString = c3_chart_internal_fn.isString = function (o) { - return typeof o === 'string'; - }, - isUndefined = c3_chart_internal_fn.isUndefined = function (v) { - return typeof v === 'undefined'; - }, - isDefined = c3_chart_internal_fn.isDefined = function (v) { - return typeof v !== 'undefined'; - }, - ceil10 = c3_chart_internal_fn.ceil10 = function (v) { - return Math.ceil(v / 10) * 10; - }, - asHalfPixel = c3_chart_internal_fn.asHalfPixel = function (n) { - return Math.ceil(n) + 0.5; - }, - diffDomain = c3_chart_internal_fn.diffDomain = function (d) { - return d[1] - d[0]; - }, - isEmpty = c3_chart_internal_fn.isEmpty = function (o) { - return typeof o === 'undefined' || o === null || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0); - }, - notEmpty = c3_chart_internal_fn.notEmpty = function (o) { - return !c3_chart_internal_fn.isEmpty(o); - }, - getOption = c3_chart_internal_fn.getOption = function (options, key, defaultValue) { - return isDefined(options[key]) ? options[key] : defaultValue; - }, - hasValue = c3_chart_internal_fn.hasValue = function (dict, value) { - var found = false; - Object.keys(dict).forEach(function (key) { - if (dict[key] === value) { found = true; } - }); - return found; - }, - sanitise = c3_chart_internal_fn.sanitise = function (str) { - return typeof str === 'string' ? str.replace(//g, '>') : str; - }, - getPathBox = c3_chart_internal_fn.getPathBox = function (path) { - var box = path.getBoundingClientRect(), - items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)], - minX = items[0].x, minY = Math.min(items[0].y, items[1].y); - return {x: minX, y: minY, width: box.width, height: box.height}; - }; - - c3_chart_fn.focus = function (targetIds) { - var $$ = this.internal, candidates; - - targetIds = $$.mapToTargetIds(targetIds); - candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), - - this.revert(); - this.defocus(); - candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); - if ($$.hasArcType()) { - $$.expandArc(targetIds); - } - $$.toggleFocusLegend(targetIds, true); - - $$.focusedTargetIds = targetIds; - $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) { - return targetIds.indexOf(id) < 0; - }); - }; - - c3_chart_fn.defocus = function (targetIds) { - var $$ = this.internal, candidates; - - targetIds = $$.mapToTargetIds(targetIds); - candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), - - candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); - if ($$.hasArcType()) { - $$.unexpandArc(targetIds); - } - $$.toggleFocusLegend(targetIds, false); - - $$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) { - return targetIds.indexOf(id) < 0; - }); - $$.defocusedTargetIds = targetIds; - }; - - c3_chart_fn.revert = function (targetIds) { - var $$ = this.internal, candidates; - - targetIds = $$.mapToTargetIds(targetIds); - candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets - - candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); - if ($$.hasArcType()) { - $$.unexpandArc(targetIds); - } - if ($$.config.legend_show) { - $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); - $$.legend.selectAll($$.selectorLegends(targetIds)) - .filter(function () { - return $$.d3.select(this).classed(CLASS.legendItemFocused); - }) - .classed(CLASS.legendItemFocused, false); - } - - $$.focusedTargetIds = []; - $$.defocusedTargetIds = []; - }; - - c3_chart_fn.show = function (targetIds, options) { - var $$ = this.internal, targets; - - targetIds = $$.mapToTargetIds(targetIds); - options = options || {}; - - $$.removeHiddenTargetIds(targetIds); - targets = $$.svg.selectAll($$.selectorTargets(targetIds)); - - targets.transition() - .style('opacity', 1, 'important') - .call($$.endall, function () { - targets.style('opacity', null).style('opacity', 1); - }); - - if (options.withLegend) { - $$.showLegend(targetIds); - } - - $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true}); - }; - - c3_chart_fn.hide = function (targetIds, options) { - var $$ = this.internal, targets; - - targetIds = $$.mapToTargetIds(targetIds); - options = options || {}; - - $$.addHiddenTargetIds(targetIds); - targets = $$.svg.selectAll($$.selectorTargets(targetIds)); - - targets.transition() - .style('opacity', 0, 'important') - .call($$.endall, function () { - targets.style('opacity', null).style('opacity', 0); - }); - - if (options.withLegend) { - $$.hideLegend(targetIds); - } - - $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true}); - }; - - c3_chart_fn.toggle = function (targetIds, options) { - var that = this, $$ = this.internal; - $$.mapToTargetIds(targetIds).forEach(function (targetId) { - $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); - }); - }; - - c3_chart_fn.zoom = function (domain) { - var $$ = this.internal; - if (domain) { - if ($$.isTimeSeries()) { - domain = domain.map(function (x) { return $$.parseDate(x); }); - } - $$.brush.extent(domain); - $$.redraw({withUpdateXDomain: true, withY: $$.config.zoom_rescale}); - $$.config.zoom_onzoom.call(this, $$.x.orgDomain()); - } - return $$.brush.extent(); - }; - c3_chart_fn.zoom.enable = function (enabled) { - var $$ = this.internal; - $$.config.zoom_enabled = enabled; - $$.updateAndRedraw(); - }; - c3_chart_fn.unzoom = function () { - var $$ = this.internal; - $$.brush.clear().update(); - $$.redraw({withUpdateXDomain: true}); - }; - - c3_chart_fn.zoom.max = function (max) { - var $$ = this.internal, config = $$.config, d3 = $$.d3; - if (max === 0 || max) { - config.zoom_x_max = d3.max([$$.orgXDomain[1], max]); - } - else { - return config.zoom_x_max; - } - }; - - c3_chart_fn.zoom.min = function (min) { - var $$ = this.internal, config = $$.config, d3 = $$.d3; - if (min === 0 || min) { - config.zoom_x_min = d3.min([$$.orgXDomain[0], min]); - } - else { - return config.zoom_x_min; - } - }; - - c3_chart_fn.zoom.range = function (range) { - if (arguments.length) { - if (isDefined(range.max)) { this.domain.max(range.max); } - if (isDefined(range.min)) { this.domain.min(range.min); } - } else { - return { - max: this.domain.max(), - min: this.domain.min() - }; - } - }; - - c3_chart_fn.load = function (args) { - var $$ = this.internal, config = $$.config; - // update xs if specified - if (args.xs) { - $$.addXs(args.xs); - } - // update names if exists - if ('names' in args) { - c3_chart_fn.data.names.bind(this)(args.names); - } - // update classes if exists - if ('classes' in args) { - Object.keys(args.classes).forEach(function (id) { - config.data_classes[id] = args.classes[id]; - }); - } - // update categories if exists - if ('categories' in args && $$.isCategorized()) { - config.axis_x_categories = args.categories; - } - // update axes if exists - if ('axes' in args) { - Object.keys(args.axes).forEach(function (id) { - config.data_axes[id] = args.axes[id]; - }); - } - // update colors if exists - if ('colors' in args) { - Object.keys(args.colors).forEach(function (id) { - config.data_colors[id] = args.colors[id]; - }); - } - // use cache if exists - if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { - $$.load($$.getCaches(args.cacheIds), args.done); - return; - } - // unload if needed - if ('unload' in args) { - // TODO: do not unload if target will load (included in url/rows/columns) - $$.unload($$.mapToTargetIds((typeof args.unload === 'boolean' && args.unload) ? null : args.unload), function () { - $$.loadFromArgs(args); - }); - } else { - $$.loadFromArgs(args); - } - }; - - c3_chart_fn.unload = function (args) { - var $$ = this.internal; - args = args || {}; - if (args instanceof Array) { - args = {ids: args}; - } else if (typeof args === 'string') { - args = {ids: [args]}; - } - $$.unload($$.mapToTargetIds(args.ids), function () { - $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true}); - if (args.done) { args.done(); } - }); - }; - - c3_chart_fn.flow = function (args) { - var $$ = this.internal, - targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(), - dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to; - - if (args.json) { - data = $$.convertJsonToData(args.json, args.keys); - } - else if (args.rows) { - data = $$.convertRowsToData(args.rows); - } - else if (args.columns) { - data = $$.convertColumnsToData(args.columns); - } - else { - return; - } - targets = $$.convertDataToTargets(data, true); - - // Update/Add data - $$.data.targets.forEach(function (t) { - var found = false, i, j; - for (i = 0; i < targets.length; i++) { - if (t.id === targets[i].id) { - found = true; - - if (t.values[t.values.length - 1]) { - tail = t.values[t.values.length - 1].index + 1; - } - length = targets[i].values.length; - - for (j = 0; j < length; j++) { - targets[i].values[j].index = tail + j; - if (!$$.isTimeSeries()) { - targets[i].values[j].x = tail + j; - } - } - t.values = t.values.concat(targets[i].values); - - targets.splice(i, 1); - break; - } - } - if (!found) { notfoundIds.push(t.id); } - }); - - // Append null for not found targets - $$.data.targets.forEach(function (t) { - var i, j; - for (i = 0; i < notfoundIds.length; i++) { - if (t.id === notfoundIds[i]) { - tail = t.values[t.values.length - 1].index + 1; - for (j = 0; j < length; j++) { - t.values.push({ - id: t.id, - index: tail + j, - x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, - value: null - }); - } - } - } - }); - - // Generate null values for new target - if ($$.data.targets.length) { - targets.forEach(function (t) { - var i, missing = []; - for (i = $$.data.targets[0].values[0].index; i < tail; i++) { - missing.push({ - id: t.id, - index: i, - x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, - value: null - }); - } - t.values.forEach(function (v) { - v.index += tail; - if (!$$.isTimeSeries()) { - v.x += tail; - } - }); - t.values = missing.concat(t.values); - }); - } - $$.data.targets = $$.data.targets.concat(targets); // add remained - - // check data count because behavior needs to change when it's only one - dataCount = $$.getMaxDataCount(); - baseTarget = $$.data.targets[0]; - baseValue = baseTarget.values[0]; - - // Update length to flow if needed - if (isDefined(args.to)) { - length = 0; - to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; - baseTarget.values.forEach(function (v) { - if (v.x < to) { length++; } - }); - } else if (isDefined(args.length)) { - length = args.length; - } - - // If only one data, update the domain to flow from left edge of the chart - if (!orgDataCount) { - if ($$.isTimeSeries()) { - if (baseTarget.values.length > 1) { - diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; - } else { - diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; - } - } else { - diff = 1; - } - domain = [baseValue.x - diff, baseValue.x]; - $$.updateXDomain(null, true, true, false, domain); - } else if (orgDataCount === 1) { - if ($$.isTimeSeries()) { - diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; - domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; - $$.updateXDomain(null, true, true, false, domain); - } - } - - // Set targets - $$.updateTargets($$.data.targets); - - // Redraw with new targets - $$.redraw({ - flow: { - index: baseValue.index, - length: length, - duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, - done: args.done, - orgDataCount: orgDataCount, - }, - withLegend: true, - withTransition: orgDataCount > 1, - withTrimXDomain: false, - withUpdateXAxis: true, - }); - }; - - c3_chart_internal_fn.generateFlow = function (args) { - var $$ = this, config = $$.config, d3 = $$.d3; - - return function () { - var targets = args.targets, - flow = args.flow, - drawBar = args.drawBar, - drawLine = args.drawLine, - drawArea = args.drawArea, - cx = args.cx, - cy = args.cy, - xv = args.xv, - xForText = args.xForText, - yForText = args.yForText, - duration = args.duration; - - var translateX, scaleX = 1, transform, - flowIndex = flow.index, - flowLength = flow.length, - flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), - flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength), - orgDomain = $$.x.domain(), domain, - durationForFlow = flow.duration || duration, - done = flow.done || function () {}, - wait = $$.generateWait(); - - var xgrid = $$.xgrid || d3.selectAll([]), - xgridLines = $$.xgridLines || d3.selectAll([]), - mainRegion = $$.mainRegion || d3.selectAll([]), - mainText = $$.mainText || d3.selectAll([]), - mainBar = $$.mainBar || d3.selectAll([]), - mainLine = $$.mainLine || d3.selectAll([]), - mainArea = $$.mainArea || d3.selectAll([]), - mainCircle = $$.mainCircle || d3.selectAll([]); - - // set flag - $$.flowing = true; - - // remove head data after rendered - $$.data.targets.forEach(function (d) { - d.values.splice(0, flowLength); - }); - - // update x domain to generate axis elements for flow - domain = $$.updateXDomain(targets, true, true); - // update elements related to x scale - if ($$.updateXGrid) { $$.updateXGrid(true); } - - // generate transform to flow - if (!flow.orgDataCount) { // if empty - if ($$.data.targets[0].values.length !== 1) { - translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); - } else { - if ($$.isTimeSeries()) { - flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0); - flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1); - translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); - } else { - translateX = diffDomain(domain) / 2; - } - } - } else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) { - translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); - } else { - if ($$.isTimeSeries()) { - translateX = ($$.x(orgDomain[0]) - $$.x(domain[0])); - } else { - translateX = ($$.x(flowStart.x) - $$.x(flowEnd.x)); - } - } - scaleX = (diffDomain(orgDomain) / diffDomain(domain)); - transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)'; - - $$.hideXGridFocus(); - - d3.transition().ease('linear').duration(durationForFlow).each(function () { - wait.add($$.axes.x.transition().call($$.xAxis)); - wait.add(mainBar.transition().attr('transform', transform)); - wait.add(mainLine.transition().attr('transform', transform)); - wait.add(mainArea.transition().attr('transform', transform)); - wait.add(mainCircle.transition().attr('transform', transform)); - wait.add(mainText.transition().attr('transform', transform)); - wait.add(mainRegion.filter($$.isRegionOnX).transition().attr('transform', transform)); - wait.add(xgrid.transition().attr('transform', transform)); - wait.add(xgridLines.transition().attr('transform', transform)); - }) - .call(wait, function () { - var i, shapes = [], texts = [], eventRects = []; - - // remove flowed elements - if (flowLength) { - for (i = 0; i < flowLength; i++) { - shapes.push('.' + CLASS.shape + '-' + (flowIndex + i)); - texts.push('.' + CLASS.text + '-' + (flowIndex + i)); - eventRects.push('.' + CLASS.eventRect + '-' + (flowIndex + i)); - } - $$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove(); - $$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove(); - $$.svg.selectAll('.' + CLASS.eventRects).selectAll(eventRects).remove(); - $$.svg.select('.' + CLASS.xgrid).remove(); - } - - // draw again for removing flowed elements and reverting attr - xgrid - .attr('transform', null) - .attr($$.xgridAttr); - xgridLines - .attr('transform', null); - xgridLines.select('line') - .attr("x1", config.axis_rotated ? 0 : xv) - .attr("x2", config.axis_rotated ? $$.width : xv); - xgridLines.select('text') - .attr("x", config.axis_rotated ? $$.width : 0) - .attr("y", xv); - mainBar - .attr('transform', null) - .attr("d", drawBar); - mainLine - .attr('transform', null) - .attr("d", drawLine); - mainArea - .attr('transform', null) - .attr("d", drawArea); - mainCircle - .attr('transform', null) - .attr("cx", cx) - .attr("cy", cy); - mainText - .attr('transform', null) - .attr('x', xForText) - .attr('y', yForText) - .style('fill-opacity', $$.opacityForText.bind($$)); - mainRegion - .attr('transform', null); - mainRegion.select('rect').filter($$.isRegionOnX) - .attr("x", $$.regionX.bind($$)) - .attr("width", $$.regionWidth.bind($$)); - - if (config.interaction_enabled) { - $$.redrawEventRect(); - } - - // callback for end of flow - done(); - - $$.flowing = false; - }); - }; - }; - - c3_chart_fn.selected = function (targetId) { - var $$ = this.internal, d3 = $$.d3; - return d3.merge( - $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape) - .filter(function () { return d3.select(this).classed(CLASS.SELECTED); }) - .map(function (d) { return d.map(function (d) { var data = d.__data__; return data.data ? data.data : data; }); }) - ); - }; - c3_chart_fn.select = function (ids, indices, resetOther) { - var $$ = this.internal, d3 = $$.d3, config = $$.config; - if (! config.data_selection_enabled) { return; } - $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { - var shape = d3.select(this), id = d.data ? d.data.id : d.id, - toggle = $$.getToggle(this, d).bind($$), - isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, - isTargetIndex = !indices || indices.indexOf(i) >= 0, - isSelected = shape.classed(CLASS.SELECTED); - // line/area selection not supported yet - if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { - return; - } - if (isTargetId && isTargetIndex) { - if (config.data_selection_isselectable(d) && !isSelected) { - toggle(true, shape.classed(CLASS.SELECTED, true), d, i); - } - } else if (isDefined(resetOther) && resetOther) { - if (isSelected) { - toggle(false, shape.classed(CLASS.SELECTED, false), d, i); - } - } - }); - }; - c3_chart_fn.unselect = function (ids, indices) { - var $$ = this.internal, d3 = $$.d3, config = $$.config; - if (! config.data_selection_enabled) { return; } - $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { - var shape = d3.select(this), id = d.data ? d.data.id : d.id, - toggle = $$.getToggle(this, d).bind($$), - isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, - isTargetIndex = !indices || indices.indexOf(i) >= 0, - isSelected = shape.classed(CLASS.SELECTED); - // line/area selection not supported yet - if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { - return; - } - if (isTargetId && isTargetIndex) { - if (config.data_selection_isselectable(d)) { - if (isSelected) { - toggle(false, shape.classed(CLASS.SELECTED, false), d, i); - } - } - } - }); - }; - - c3_chart_fn.transform = function (type, targetIds) { - var $$ = this.internal, - options = ['pie', 'donut'].indexOf(type) >= 0 ? {withTransform: true} : null; - $$.transformTo(targetIds, type, options); - }; - - c3_chart_internal_fn.transformTo = function (targetIds, type, optionsForRedraw) { - var $$ = this, - withTransitionForAxis = !$$.hasArcType(), - options = optionsForRedraw || {withTransitionForAxis: withTransitionForAxis}; - options.withTransitionForTransform = false; - $$.transiting = false; - $$.setTargetType(targetIds, type); - $$.updateTargets($$.data.targets); // this is needed when transforming to arc - $$.updateAndRedraw(options); - }; - - c3_chart_fn.groups = function (groups) { - var $$ = this.internal, config = $$.config; - if (isUndefined(groups)) { return config.data_groups; } - config.data_groups = groups; - $$.redraw(); - return config.data_groups; - }; - - c3_chart_fn.xgrids = function (grids) { - var $$ = this.internal, config = $$.config; - if (! grids) { return config.grid_x_lines; } - config.grid_x_lines = grids; - $$.redrawWithoutRescale(); - return config.grid_x_lines; - }; - c3_chart_fn.xgrids.add = function (grids) { - var $$ = this.internal; - return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])); - }; - c3_chart_fn.xgrids.remove = function (params) { // TODO: multiple - var $$ = this.internal; - $$.removeGridLines(params, true); - }; - - c3_chart_fn.ygrids = function (grids) { - var $$ = this.internal, config = $$.config; - if (! grids) { return config.grid_y_lines; } - config.grid_y_lines = grids; - $$.redrawWithoutRescale(); - return config.grid_y_lines; - }; - c3_chart_fn.ygrids.add = function (grids) { - var $$ = this.internal; - return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])); - }; - c3_chart_fn.ygrids.remove = function (params) { // TODO: multiple - var $$ = this.internal; - $$.removeGridLines(params, false); - }; - - c3_chart_fn.regions = function (regions) { - var $$ = this.internal, config = $$.config; - if (!regions) { return config.regions; } - config.regions = regions; - $$.redrawWithoutRescale(); - return config.regions; - }; - c3_chart_fn.regions.add = function (regions) { - var $$ = this.internal, config = $$.config; - if (!regions) { return config.regions; } - config.regions = config.regions.concat(regions); - $$.redrawWithoutRescale(); - return config.regions; - }; - c3_chart_fn.regions.remove = function (options) { - var $$ = this.internal, config = $$.config, - duration, classes, regions; - - options = options || {}; - duration = $$.getOption(options, "duration", config.transition_duration); - classes = $$.getOption(options, "classes", [CLASS.region]); - - regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) { return '.' + c; })); - (duration ? regions.transition().duration(duration) : regions) - .style('opacity', 0) - .remove(); - - config.regions = config.regions.filter(function (region) { - var found = false; - if (!region['class']) { - return true; - } - region['class'].split(' ').forEach(function (c) { - if (classes.indexOf(c) >= 0) { found = true; } - }); - return !found; - }); - - return config.regions; - }; - - c3_chart_fn.data = function (targetIds) { - var targets = this.internal.data.targets; - return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) { - return [].concat(targetIds).indexOf(t.id) >= 0; - }); - }; - c3_chart_fn.data.shown = function (targetIds) { - return this.internal.filterTargetsToShow(this.data(targetIds)); - }; - c3_chart_fn.data.values = function (targetId) { - var targets, values = null; - if (targetId) { - targets = this.data(targetId); - values = targets[0] ? targets[0].values.map(function (d) { return d.value; }) : null; - } - return values; - }; - c3_chart_fn.data.names = function (names) { - this.internal.clearLegendItemTextBoxCache(); - return this.internal.updateDataAttributes('names', names); - }; - c3_chart_fn.data.colors = function (colors) { - return this.internal.updateDataAttributes('colors', colors); - }; - c3_chart_fn.data.axes = function (axes) { - return this.internal.updateDataAttributes('axes', axes); - }; - - c3_chart_fn.category = function (i, category) { - var $$ = this.internal, config = $$.config; - if (arguments.length > 1) { - config.axis_x_categories[i] = category; - $$.redraw(); - } - return config.axis_x_categories[i]; - }; - c3_chart_fn.categories = function (categories) { - var $$ = this.internal, config = $$.config; - if (!arguments.length) { return config.axis_x_categories; } - config.axis_x_categories = categories; - $$.redraw(); - return config.axis_x_categories; - }; - - // TODO: fix - c3_chart_fn.color = function (id) { - var $$ = this.internal; - return $$.color(id); // more patterns - }; - - c3_chart_fn.x = function (x) { - var $$ = this.internal; - if (arguments.length) { - $$.updateTargetX($$.data.targets, x); - $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true}); - } - return $$.data.xs; - }; - c3_chart_fn.xs = function (xs) { - var $$ = this.internal; - if (arguments.length) { - $$.updateTargetXs($$.data.targets, xs); - $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true}); - } - return $$.data.xs; - }; - - c3_chart_fn.axis = function () {}; - c3_chart_fn.axis.labels = function (labels) { - var $$ = this.internal; - if (arguments.length) { - Object.keys(labels).forEach(function (axisId) { - $$.axis.setLabelText(axisId, labels[axisId]); - }); - $$.axis.updateLabels(); - } - // TODO: return some values? - }; - c3_chart_fn.axis.max = function (max) { - var $$ = this.internal, config = $$.config; - if (arguments.length) { - if (typeof max === 'object') { - if (isValue(max.x)) { config.axis_x_max = max.x; } - if (isValue(max.y)) { config.axis_y_max = max.y; } - if (isValue(max.y2)) { config.axis_y2_max = max.y2; } - } else { - config.axis_y_max = config.axis_y2_max = max; - } - $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true}); - } else { - return { - x: config.axis_x_max, - y: config.axis_y_max, - y2: config.axis_y2_max - }; - } - }; - c3_chart_fn.axis.min = function (min) { - var $$ = this.internal, config = $$.config; - if (arguments.length) { - if (typeof min === 'object') { - if (isValue(min.x)) { config.axis_x_min = min.x; } - if (isValue(min.y)) { config.axis_y_min = min.y; } - if (isValue(min.y2)) { config.axis_y2_min = min.y2; } - } else { - config.axis_y_min = config.axis_y2_min = min; - } - $$.redraw({withUpdateOrgXDomain: true, withUpdateXDomain: true}); - } else { - return { - x: config.axis_x_min, - y: config.axis_y_min, - y2: config.axis_y2_min - }; - } - }; - c3_chart_fn.axis.range = function (range) { - if (arguments.length) { - if (isDefined(range.max)) { this.axis.max(range.max); } - if (isDefined(range.min)) { this.axis.min(range.min); } - } else { - return { - max: this.axis.max(), - min: this.axis.min() - }; - } - }; - - c3_chart_fn.legend = function () {}; - c3_chart_fn.legend.show = function (targetIds) { - var $$ = this.internal; - $$.showLegend($$.mapToTargetIds(targetIds)); - $$.updateAndRedraw({withLegend: true}); - }; - c3_chart_fn.legend.hide = function (targetIds) { - var $$ = this.internal; - $$.hideLegend($$.mapToTargetIds(targetIds)); - $$.updateAndRedraw({withLegend: true}); - }; - - c3_chart_fn.resize = function (size) { - var $$ = this.internal, config = $$.config; - config.size_width = size ? size.width : null; - config.size_height = size ? size.height : null; - this.flush(); - }; - - c3_chart_fn.flush = function () { - var $$ = this.internal; - $$.updateAndRedraw({withLegend: true, withTransition: false, withTransitionForTransform: false}); - }; - - c3_chart_fn.destroy = function () { - var $$ = this.internal; - - window.clearInterval($$.intervalForObserveInserted); - - if ($$.resizeTimeout !== undefined) { - window.clearTimeout($$.resizeTimeout); - } - - if (window.detachEvent) { - window.detachEvent('onresize', $$.resizeFunction); - } else if (window.removeEventListener) { - window.removeEventListener('resize', $$.resizeFunction); - } else { - var wrapper = window.onresize; - // check if no one else removed our wrapper and remove our resizeFunction from it - if (wrapper && wrapper.add && wrapper.remove) { - wrapper.remove($$.resizeFunction); - } - } - - $$.selectChart.classed('c3', false).html(""); - - // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen. - Object.keys($$).forEach(function (key) { - $$[key] = null; - }); - - return null; - }; - - c3_chart_fn.tooltip = function () {}; - c3_chart_fn.tooltip.show = function (args) { - var $$ = this.internal, index, mouse; - - // determine mouse position on the chart - if (args.mouse) { - mouse = args.mouse; - } - - // determine focus data - if (args.data) { - if ($$.isMultipleX()) { - // if multiple xs, target point will be determined by mouse - mouse = [$$.x(args.data.x), $$.getYScale(args.data.id)(args.data.value)]; - index = null; - } else { - // TODO: when tooltip_grouped = false - index = isValue(args.data.index) ? args.data.index : $$.getIndexByX(args.data.x); - } - } - else if (typeof args.x !== 'undefined') { - index = $$.getIndexByX(args.x); - } - else if (typeof args.index !== 'undefined') { - index = args.index; - } - - // emulate mouse events to show - $$.dispatchEvent('mouseover', index, mouse); - $$.dispatchEvent('mousemove', index, mouse); - - $$.config.tooltip_onshow.call($$, args.data); - }; - c3_chart_fn.tooltip.hide = function () { - // TODO: get target data by checking the state of focus - this.internal.dispatchEvent('mouseout', 0); - - this.internal.config.tooltip_onhide.call(this); - }; - - // Features: - // 1. category axis - // 2. ceil values of translate/x/y to int for half pixel antialiasing - // 3. multiline tick text - var tickTextCharSize; - function c3_axis(d3, params) { - var scale = d3.scale.linear(), orient = "bottom", innerTickSize = 6, outerTickSize, tickPadding = 3, tickValues = null, tickFormat, tickArguments; - - var tickOffset = 0, tickCulling = true, tickCentered; - - params = params || {}; - outerTickSize = params.withOuterTick ? 6 : 0; - - function axisX(selection, x) { - selection.attr("transform", function (d) { - return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)"; - }); - } - function axisY(selection, y) { - selection.attr("transform", function (d) { - return "translate(0," + Math.ceil(y(d)) + ")"; - }); - } - function scaleExtent(domain) { - var start = domain[0], stop = domain[domain.length - 1]; - return start < stop ? [ start, stop ] : [ stop, start ]; - } - function generateTicks(scale) { - var i, domain, ticks = []; - if (scale.ticks) { - return scale.ticks.apply(scale, tickArguments); - } - domain = scale.domain(); - for (i = Math.ceil(domain[0]); i < domain[1]; i++) { - ticks.push(i); - } - if (ticks.length > 0 && ticks[0] > 0) { - ticks.unshift(ticks[0] - (ticks[1] - ticks[0])); - } - return ticks; - } - function copyScale() { - var newScale = scale.copy(), domain; - if (params.isCategory) { - domain = scale.domain(); - newScale.domain([domain[0], domain[1] - 1]); - } - return newScale; - } - function textFormatted(v) { - var formatted = tickFormat ? tickFormat(v) : v; - return typeof formatted !== 'undefined' ? formatted : ''; - } - function getSizeFor1Char(tick) { - if (tickTextCharSize) { - return tickTextCharSize; - } - var size = { - h: 11.5, - w: 5.5 - }; - tick.select('text').text(textFormatted).each(function (d) { - var box = this.getBoundingClientRect(), - text = textFormatted(d), - h = box.height, - w = text ? (box.width / text.length) : undefined; - if (h && w) { - size.h = h; - size.w = w; - } - }).text(''); - tickTextCharSize = size; - return size; - } - function transitionise(selection) { - return params.withoutTransition ? selection : d3.transition(selection); - } - function axis(g) { - g.each(function () { - var g = axis.g = d3.select(this); - - var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = copyScale(); - - var ticks = tickValues ? tickValues : generateTicks(scale1), - tick = g.selectAll(".tick").data(ticks, scale1), - tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6), - // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks. - tickExit = tick.exit().remove(), - tickUpdate = transitionise(tick).style("opacity", 1), - tickTransform, tickX, tickY; - - var range = scale.rangeExtent ? scale.rangeExtent() : scaleExtent(scale.range()), - path = g.selectAll(".domain").data([ 0 ]), - pathUpdate = (path.enter().append("path").attr("class", "domain"), transitionise(path)); - tickEnter.append("line"); - tickEnter.append("text"); - - var lineEnter = tickEnter.select("line"), - lineUpdate = tickUpdate.select("line"), - textEnter = tickEnter.select("text"), - textUpdate = tickUpdate.select("text"); - - if (params.isCategory) { - tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2); - tickX = tickCentered ? 0 : tickOffset; - tickY = tickCentered ? tickOffset : 0; - } else { - tickOffset = tickX = 0; - } - - var text, tspan, sizeFor1Char = getSizeFor1Char(g.select('.tick')), counts = []; - var tickLength = Math.max(innerTickSize, 0) + tickPadding, - isVertical = orient === 'left' || orient === 'right'; - - // this should be called only when category axis - function splitTickText(d, maxWidth) { - var tickText = textFormatted(d), - subtext, spaceIndex, textWidth, splitted = []; - - if (Object.prototype.toString.call(tickText) === "[object Array]") { - return tickText; - } - - if (!maxWidth || maxWidth <= 0) { - maxWidth = isVertical ? 95 : params.isCategory ? (Math.ceil(scale1(ticks[1]) - scale1(ticks[0])) - 12) : 110; - } - - function split(splitted, text) { - spaceIndex = undefined; - for (var i = 1; i < text.length; i++) { - if (text.charAt(i) === ' ') { - spaceIndex = i; - } - subtext = text.substr(0, i + 1); - textWidth = sizeFor1Char.w * subtext.length; - // if text width gets over tick width, split by space index or crrent index - if (maxWidth < textWidth) { - return split( - splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), - text.slice(spaceIndex ? spaceIndex + 1 : i) - ); - } - } - return splitted.concat(text); - } - - return split(splitted, tickText + ""); - } - - function tspanDy(d, i) { - var dy = sizeFor1Char.h; - if (i === 0) { - if (orient === 'left' || orient === 'right') { - dy = -((counts[d.index] - 1) * (sizeFor1Char.h / 2) - 3); - } else { - dy = ".71em"; - } - } - return dy; - } - - function tickSize(d) { - var tickPosition = scale(d) + (tickCentered ? 0 : tickOffset); - return range[0] < tickPosition && tickPosition < range[1] ? innerTickSize : 0; - } - - text = tick.select("text"); - tspan = text.selectAll('tspan') - .data(function (d, i) { - var splitted = params.tickMultiline ? splitTickText(d, params.tickWidth) : [].concat(textFormatted(d)); - counts[i] = splitted.length; - return splitted.map(function (s) { - return { index: i, splitted: s }; - }); - }); - tspan.enter().append('tspan'); - tspan.exit().remove(); - tspan.text(function (d) { return d.splitted; }); - - var rotate = params.tickTextRotate; - - function textAnchorForText(rotate) { - if (!rotate) { - return 'middle'; - } - return rotate > 0 ? "start" : "end"; - } - function textTransform(rotate) { - if (!rotate) { - return ''; - } - return "rotate(" + rotate + ")"; - } - function dxForText(rotate) { - if (!rotate) { - return 0; - } - return 8 * Math.sin(Math.PI * (rotate / 180)); - } - function yForText(rotate) { - if (!rotate) { - return tickLength; - } - return 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1); - } - - switch (orient) { - case "bottom": - { - tickTransform = axisX; - lineEnter.attr("y2", innerTickSize); - textEnter.attr("y", tickLength); - lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize); - textUpdate.attr("x", 0).attr("y", yForText(rotate)) - .style("text-anchor", textAnchorForText(rotate)) - .attr("transform", textTransform(rotate)); - tspan.attr('x', 0).attr("dy", tspanDy).attr('dx', dxForText(rotate)); - pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize); - break; - } - case "top": - { - // TODO: rotated tick text - tickTransform = axisX; - lineEnter.attr("y2", -innerTickSize); - textEnter.attr("y", -tickLength); - lineUpdate.attr("x2", 0).attr("y2", -innerTickSize); - textUpdate.attr("x", 0).attr("y", -tickLength); - text.style("text-anchor", "middle"); - tspan.attr('x', 0).attr("dy", "0em"); - pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize); - break; - } - case "left": - { - tickTransform = axisY; - lineEnter.attr("x2", -innerTickSize); - textEnter.attr("x", -tickLength); - lineUpdate.attr("x2", -innerTickSize).attr("y1", tickY).attr("y2", tickY); - textUpdate.attr("x", -tickLength).attr("y", tickOffset); - text.style("text-anchor", "end"); - tspan.attr('x', -tickLength).attr("dy", tspanDy); - pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize); - break; - } - case "right": - { - tickTransform = axisY; - lineEnter.attr("x2", innerTickSize); - textEnter.attr("x", tickLength); - lineUpdate.attr("x2", innerTickSize).attr("y2", 0); - textUpdate.attr("x", tickLength).attr("y", 0); - text.style("text-anchor", "start"); - tspan.attr('x', tickLength).attr("dy", tspanDy); - pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize); - break; - } - } - if (scale1.rangeBand) { - var x = scale1, dx = x.rangeBand() / 2; - scale0 = scale1 = function (d) { - return x(d) + dx; - }; - } else if (scale0.rangeBand) { - scale0 = scale1; - } else { - tickExit.call(tickTransform, scale1); - } - tickEnter.call(tickTransform, scale0); - tickUpdate.call(tickTransform, scale1); - }); - } - axis.scale = function (x) { - if (!arguments.length) { return scale; } - scale = x; - return axis; - }; - axis.orient = function (x) { - if (!arguments.length) { return orient; } - orient = x in {top: 1, right: 1, bottom: 1, left: 1} ? x + "" : "bottom"; - return axis; - }; - axis.tickFormat = function (format) { - if (!arguments.length) { return tickFormat; } - tickFormat = format; - return axis; - }; - axis.tickCentered = function (isCentered) { - if (!arguments.length) { return tickCentered; } - tickCentered = isCentered; - return axis; - }; - axis.tickOffset = function () { - return tickOffset; - }; - axis.tickInterval = function () { - var interval, length; - if (params.isCategory) { - interval = tickOffset * 2; - } - else { - length = axis.g.select('path.domain').node().getTotalLength() - outerTickSize * 2; - interval = length / axis.g.selectAll('line').size(); - } - return interval === Infinity ? 0 : interval; - }; - axis.ticks = function () { - if (!arguments.length) { return tickArguments; } - tickArguments = arguments; - return axis; - }; - axis.tickCulling = function (culling) { - if (!arguments.length) { return tickCulling; } - tickCulling = culling; - return axis; - }; - axis.tickValues = function (x) { - if (typeof x === 'function') { - tickValues = function () { - return x(scale.domain()); - }; - } - else { - if (!arguments.length) { return tickValues; } - tickValues = x; - } - return axis; - }; - return axis; - } - - c3_chart_internal_fn.isSafari = function () { - var ua = window.navigator.userAgent; - return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0; - }; - c3_chart_internal_fn.isChrome = function () { - var ua = window.navigator.userAgent; - return ua.indexOf('Chrome') >= 0; - }; - - /* jshint ignore:start */ - - // PhantomJS doesn't have support for Function.prototype.bind, which has caused confusion. Use - // this polyfill to avoid the confusion. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill - - if (!Function.prototype.bind) { - Function.prototype.bind = function(oThis) { - if (typeof this !== 'function') { - // closest thing possible to the ECMAScript 5 - // internal IsCallable function - throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function() {}, - fBound = function() { - return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; - } - - //SVGPathSeg API polyfill - //https://github.com/progers/pathseg - // - //This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from - //SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec - //changes which were implemented in Firefox 43 and Chrome 46. - //Chrome 48 removes these APIs, so this polyfill is required. - - (function() { "use strict"; - if (!("SVGPathSeg" in window)) { - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg - window.SVGPathSeg = function(type, typeAsLetter, owningPathSegList) { - this.pathSegType = type; - this.pathSegTypeAsLetter = typeAsLetter; - this._owningPathSegList = owningPathSegList; - } - - SVGPathSeg.PATHSEG_UNKNOWN = 0; - SVGPathSeg.PATHSEG_CLOSEPATH = 1; - SVGPathSeg.PATHSEG_MOVETO_ABS = 2; - SVGPathSeg.PATHSEG_MOVETO_REL = 3; - SVGPathSeg.PATHSEG_LINETO_ABS = 4; - SVGPathSeg.PATHSEG_LINETO_REL = 5; - SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; - SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; - SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; - SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; - SVGPathSeg.PATHSEG_ARC_ABS = 10; - SVGPathSeg.PATHSEG_ARC_REL = 11; - SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; - SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; - SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; - SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; - SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; - SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; - SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; - SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; - - // Notify owning PathSegList on any changes so they can be synchronized back to the path element. - SVGPathSeg.prototype._segmentChanged = function() { - if (this._owningPathSegList) - this._owningPathSegList.segmentChanged(this); - } - - window.SVGPathSegClosePath = function(owningPathSegList) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList); - } - SVGPathSegClosePath.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegClosePath.prototype.toString = function() { return "[object SVGPathSegClosePath]"; } - SVGPathSegClosePath.prototype._asPathString = function() { return this.pathSegTypeAsLetter; } - SVGPathSegClosePath.prototype.clone = function() { return new SVGPathSegClosePath(undefined); } - - window.SVGPathSegMovetoAbs = function(owningPathSegList, x, y) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList); - this._x = x; - this._y = y; - } - SVGPathSegMovetoAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegMovetoAbs.prototype.toString = function() { return "[object SVGPathSegMovetoAbs]"; } - SVGPathSegMovetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } - SVGPathSegMovetoAbs.prototype.clone = function() { return new SVGPathSegMovetoAbs(undefined, this._x, this._y); } - Object.defineProperty(SVGPathSegMovetoAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegMovetoAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegMovetoRel = function(owningPathSegList, x, y) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList); - this._x = x; - this._y = y; - } - SVGPathSegMovetoRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegMovetoRel.prototype.toString = function() { return "[object SVGPathSegMovetoRel]"; } - SVGPathSegMovetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } - SVGPathSegMovetoRel.prototype.clone = function() { return new SVGPathSegMovetoRel(undefined, this._x, this._y); } - Object.defineProperty(SVGPathSegMovetoRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegMovetoRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegLinetoAbs = function(owningPathSegList, x, y) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList); - this._x = x; - this._y = y; - } - SVGPathSegLinetoAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegLinetoAbs.prototype.toString = function() { return "[object SVGPathSegLinetoAbs]"; } - SVGPathSegLinetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } - SVGPathSegLinetoAbs.prototype.clone = function() { return new SVGPathSegLinetoAbs(undefined, this._x, this._y); } - Object.defineProperty(SVGPathSegLinetoAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegLinetoAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegLinetoRel = function(owningPathSegList, x, y) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList); - this._x = x; - this._y = y; - } - SVGPathSegLinetoRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegLinetoRel.prototype.toString = function() { return "[object SVGPathSegLinetoRel]"; } - SVGPathSegLinetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } - SVGPathSegLinetoRel.prototype.clone = function() { return new SVGPathSegLinetoRel(undefined, this._x, this._y); } - Object.defineProperty(SVGPathSegLinetoRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegLinetoRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegCurvetoCubicAbs = function(owningPathSegList, x, y, x1, y1, x2, y2) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList); - this._x = x; - this._y = y; - this._x1 = x1; - this._y1 = y1; - this._x2 = x2; - this._y2 = y2; - } - SVGPathSegCurvetoCubicAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegCurvetoCubicAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicAbs]"; } - SVGPathSegCurvetoCubicAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; } - SVGPathSegCurvetoCubicAbs.prototype.clone = function() { return new SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); } - Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicAbs.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegCurvetoCubicRel = function(owningPathSegList, x, y, x1, y1, x2, y2) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList); - this._x = x; - this._y = y; - this._x1 = x1; - this._y1 = y1; - this._x2 = x2; - this._y2 = y2; - } - SVGPathSegCurvetoCubicRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegCurvetoCubicRel.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicRel]"; } - SVGPathSegCurvetoCubicRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; } - SVGPathSegCurvetoCubicRel.prototype.clone = function() { return new SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); } - Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicRel.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegCurvetoQuadraticAbs = function(owningPathSegList, x, y, x1, y1) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList); - this._x = x; - this._y = y; - this._x1 = x1; - this._y1 = y1; - } - SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegCurvetoQuadraticAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticAbs]"; } - SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; } - SVGPathSegCurvetoQuadraticAbs.prototype.clone = function() { return new SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); } - Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoQuadraticAbs.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegCurvetoQuadraticRel = function(owningPathSegList, x, y, x1, y1) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList); - this._x = x; - this._y = y; - this._x1 = x1; - this._y1 = y1; - } - SVGPathSegCurvetoQuadraticRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegCurvetoQuadraticRel.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticRel]"; } - SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; } - SVGPathSegCurvetoQuadraticRel.prototype.clone = function() { return new SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); } - Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoQuadraticRel.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegArcAbs = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList); - this._x = x; - this._y = y; - this._r1 = r1; - this._r2 = r2; - this._angle = angle; - this._largeArcFlag = largeArcFlag; - this._sweepFlag = sweepFlag; - } - SVGPathSegArcAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegArcAbs.prototype.toString = function() { return "[object SVGPathSegArcAbs]"; } - SVGPathSegArcAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; } - SVGPathSegArcAbs.prototype.clone = function() { return new SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); } - Object.defineProperty(SVGPathSegArcAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcAbs.prototype, "r1", { get: function() { return this._r1; }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcAbs.prototype, "r2", { get: function() { return this._r2; }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcAbs.prototype, "angle", { get: function() { return this._angle; }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcAbs.prototype, "largeArcFlag", { get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcAbs.prototype, "sweepFlag", { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegArcRel = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList); - this._x = x; - this._y = y; - this._r1 = r1; - this._r2 = r2; - this._angle = angle; - this._largeArcFlag = largeArcFlag; - this._sweepFlag = sweepFlag; - } - SVGPathSegArcRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegArcRel.prototype.toString = function() { return "[object SVGPathSegArcRel]"; } - SVGPathSegArcRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; } - SVGPathSegArcRel.prototype.clone = function() { return new SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); } - Object.defineProperty(SVGPathSegArcRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcRel.prototype, "r1", { get: function() { return this._r1; }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcRel.prototype, "r2", { get: function() { return this._r2; }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcRel.prototype, "angle", { get: function() { return this._angle; }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcRel.prototype, "largeArcFlag", { get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegArcRel.prototype, "sweepFlag", { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegLinetoHorizontalAbs = function(owningPathSegList, x) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList); - this._x = x; - } - SVGPathSegLinetoHorizontalAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegLinetoHorizontalAbs.prototype.toString = function() { return "[object SVGPathSegLinetoHorizontalAbs]"; } - SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x; } - SVGPathSegLinetoHorizontalAbs.prototype.clone = function() { return new SVGPathSegLinetoHorizontalAbs(undefined, this._x); } - Object.defineProperty(SVGPathSegLinetoHorizontalAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegLinetoHorizontalRel = function(owningPathSegList, x) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList); - this._x = x; - } - SVGPathSegLinetoHorizontalRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegLinetoHorizontalRel.prototype.toString = function() { return "[object SVGPathSegLinetoHorizontalRel]"; } - SVGPathSegLinetoHorizontalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x; } - SVGPathSegLinetoHorizontalRel.prototype.clone = function() { return new SVGPathSegLinetoHorizontalRel(undefined, this._x); } - Object.defineProperty(SVGPathSegLinetoHorizontalRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegLinetoVerticalAbs = function(owningPathSegList, y) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList); - this._y = y; - } - SVGPathSegLinetoVerticalAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegLinetoVerticalAbs.prototype.toString = function() { return "[object SVGPathSegLinetoVerticalAbs]"; } - SVGPathSegLinetoVerticalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._y; } - SVGPathSegLinetoVerticalAbs.prototype.clone = function() { return new SVGPathSegLinetoVerticalAbs(undefined, this._y); } - Object.defineProperty(SVGPathSegLinetoVerticalAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegLinetoVerticalRel = function(owningPathSegList, y) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList); - this._y = y; - } - SVGPathSegLinetoVerticalRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegLinetoVerticalRel.prototype.toString = function() { return "[object SVGPathSegLinetoVerticalRel]"; } - SVGPathSegLinetoVerticalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._y; } - SVGPathSegLinetoVerticalRel.prototype.clone = function() { return new SVGPathSegLinetoVerticalRel(undefined, this._y); } - Object.defineProperty(SVGPathSegLinetoVerticalRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegCurvetoCubicSmoothAbs = function(owningPathSegList, x, y, x2, y2) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList); - this._x = x; - this._y = y; - this._x2 = x2; - this._y2 = y2; - } - SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicSmoothAbs]"; } - SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; } - SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function() { return new SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); } - Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegCurvetoCubicSmoothRel = function(owningPathSegList, x, y, x2, y2) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList); - this._x = x; - this._y = y; - this._x2 = x2; - this._y2 = y2; - } - SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicSmoothRel]"; } - SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; } - SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function() { return new SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); } - Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegCurvetoQuadraticSmoothAbs = function(owningPathSegList, x, y) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList); - this._x = x; - this._y = y; - } - SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticSmoothAbs]"; } - SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } - SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function() { return new SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); } - Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - - window.SVGPathSegCurvetoQuadraticSmoothRel = function(owningPathSegList, x, y) { - SVGPathSeg.call(this, SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList); - this._x = x; - this._y = y; - } - SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(SVGPathSeg.prototype); - SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticSmoothRel]"; } - SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; } - SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function() { return new SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); } - Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); - Object.defineProperty(SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); - - // Add createSVGPathSeg* functions to SVGPathElement. - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathElement. - SVGPathElement.prototype.createSVGPathSegClosePath = function() { return new SVGPathSegClosePath(undefined); } - SVGPathElement.prototype.createSVGPathSegMovetoAbs = function(x, y) { return new SVGPathSegMovetoAbs(undefined, x, y); } - SVGPathElement.prototype.createSVGPathSegMovetoRel = function(x, y) { return new SVGPathSegMovetoRel(undefined, x, y); } - SVGPathElement.prototype.createSVGPathSegLinetoAbs = function(x, y) { return new SVGPathSegLinetoAbs(undefined, x, y); } - SVGPathElement.prototype.createSVGPathSegLinetoRel = function(x, y) { return new SVGPathSegLinetoRel(undefined, x, y); } - SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function(x, y, x1, y1, x2, y2) { return new SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); } - SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function(x, y, x1, y1, x2, y2) { return new SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); } - SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function(x, y, x1, y1) { return new SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); } - SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function(x, y, x1, y1) { return new SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); } - SVGPathElement.prototype.createSVGPathSegArcAbs = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); } - SVGPathElement.prototype.createSVGPathSegArcRel = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); } - SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function(x) { return new SVGPathSegLinetoHorizontalAbs(undefined, x); } - SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function(x) { return new SVGPathSegLinetoHorizontalRel(undefined, x); } - SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function(y) { return new SVGPathSegLinetoVerticalAbs(undefined, y); } - SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function(y) { return new SVGPathSegLinetoVerticalRel(undefined, y); } - SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function(x, y, x2, y2) { return new SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); } - SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function(x, y, x2, y2) { return new SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); } - SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function(x, y) { return new SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); } - SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function(x, y) { return new SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); } - } - - if (!("SVGPathSegList" in window)) { - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList - window.SVGPathSegList = function(pathElement) { - this._pathElement = pathElement; - this._list = this._parsePath(this._pathElement.getAttribute("d")); - - // Use a MutationObserver to catch changes to the path's "d" attribute. - this._mutationObserverConfig = { "attributes": true, "attributeFilter": ["d"] }; - this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this)); - this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); - } - - Object.defineProperty(SVGPathSegList.prototype, "numberOfItems", { - get: function() { - this._checkPathSynchronizedToList(); - return this._list.length; - }, - enumerable: true - }); - - // Add the pathSegList accessors to SVGPathElement. - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData - Object.defineProperty(SVGPathElement.prototype, "pathSegList", { - get: function() { - if (!this._pathSegList) - this._pathSegList = new SVGPathSegList(this); - return this._pathSegList; - }, - enumerable: true - }); - // FIXME: The following are not implemented and simply return SVGPathElement.pathSegList. - Object.defineProperty(SVGPathElement.prototype, "normalizedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true }); - Object.defineProperty(SVGPathElement.prototype, "animatedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true }); - Object.defineProperty(SVGPathElement.prototype, "animatedNormalizedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true }); - - // Process any pending mutations to the path element and update the list as needed. - // This should be the first call of all public functions and is needed because - // MutationObservers are not synchronous so we can have pending asynchronous mutations. - SVGPathSegList.prototype._checkPathSynchronizedToList = function() { - this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords()); - } - - SVGPathSegList.prototype._updateListFromPathMutations = function(mutationRecords) { - if (!this._pathElement) - return; - var hasPathMutations = false; - mutationRecords.forEach(function(record) { - if (record.attributeName == "d") - hasPathMutations = true; - }); - if (hasPathMutations) - this._list = this._parsePath(this._pathElement.getAttribute("d")); - } - - // Serialize the list and update the path's 'd' attribute. - SVGPathSegList.prototype._writeListToPath = function() { - this._pathElementMutationObserver.disconnect(); - this._pathElement.setAttribute("d", SVGPathSegList._pathSegArrayAsString(this._list)); - this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); - } - - // When a path segment changes the list needs to be synchronized back to the path element. - SVGPathSegList.prototype.segmentChanged = function(pathSeg) { - this._writeListToPath(); - } - - SVGPathSegList.prototype.clear = function() { - this._checkPathSynchronizedToList(); - - this._list.forEach(function(pathSeg) { - pathSeg._owningPathSegList = null; - }); - this._list = []; - this._writeListToPath(); - } - - SVGPathSegList.prototype.initialize = function(newItem) { - this._checkPathSynchronizedToList(); - - this._list = [newItem]; - newItem._owningPathSegList = this; - this._writeListToPath(); - return newItem; - } - - SVGPathSegList.prototype._checkValidIndex = function(index) { - if (isNaN(index) || index < 0 || index >= this.numberOfItems) - throw "INDEX_SIZE_ERR"; - } - - SVGPathSegList.prototype.getItem = function(index) { - this._checkPathSynchronizedToList(); - - this._checkValidIndex(index); - return this._list[index]; - } - - SVGPathSegList.prototype.insertItemBefore = function(newItem, index) { - this._checkPathSynchronizedToList(); - - // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. - if (index > this.numberOfItems) - index = this.numberOfItems; - if (newItem._owningPathSegList) { - // SVG2 spec says to make a copy. - newItem = newItem.clone(); - } - this._list.splice(index, 0, newItem); - newItem._owningPathSegList = this; - this._writeListToPath(); - return newItem; - } - - SVGPathSegList.prototype.replaceItem = function(newItem, index) { - this._checkPathSynchronizedToList(); - - if (newItem._owningPathSegList) { - // SVG2 spec says to make a copy. - newItem = newItem.clone(); - } - this._checkValidIndex(index); - this._list[index] = newItem; - newItem._owningPathSegList = this; - this._writeListToPath(); - return newItem; - } - - SVGPathSegList.prototype.removeItem = function(index) { - this._checkPathSynchronizedToList(); - - this._checkValidIndex(index); - var item = this._list[index]; - this._list.splice(index, 1); - this._writeListToPath(); - return item; - } - - SVGPathSegList.prototype.appendItem = function(newItem) { - this._checkPathSynchronizedToList(); - - if (newItem._owningPathSegList) { - // SVG2 spec says to make a copy. - newItem = newItem.clone(); - } - this._list.push(newItem); - newItem._owningPathSegList = this; - // TODO: Optimize this to just append to the existing attribute. - this._writeListToPath(); - return newItem; - } - - SVGPathSegList._pathSegArrayAsString = function(pathSegArray) { - var string = ""; - var first = true; - pathSegArray.forEach(function(pathSeg) { - if (first) { - first = false; - string += pathSeg._asPathString(); - } else { - string += " " + pathSeg._asPathString(); - } - }); - return string; - } - - // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. - SVGPathSegList.prototype._parsePath = function(string) { - if (!string || string.length == 0) - return []; - - var owningPathSegList = this; - - var Builder = function() { - this.pathSegList = []; - } - - Builder.prototype.appendSegment = function(pathSeg) { - this.pathSegList.push(pathSeg); - } - - var Source = function(string) { - this._string = string; - this._currentIndex = 0; - this._endIndex = this._string.length; - this._previousCommand = SVGPathSeg.PATHSEG_UNKNOWN; - - this._skipOptionalSpaces(); - } - - Source.prototype._isCurrentSpace = function() { - var character = this._string[this._currentIndex]; - return character <= " " && (character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f"); - } - - Source.prototype._skipOptionalSpaces = function() { - while (this._currentIndex < this._endIndex && this._isCurrentSpace()) - this._currentIndex++; - return this._currentIndex < this._endIndex; - } - - Source.prototype._skipOptionalSpacesOrDelimiter = function() { - if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ",") - return false; - if (this._skipOptionalSpaces()) { - if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ",") { - this._currentIndex++; - this._skipOptionalSpaces(); - } - } - return this._currentIndex < this._endIndex; - } - - Source.prototype.hasMoreData = function() { - return this._currentIndex < this._endIndex; - } - - Source.prototype.peekSegmentType = function() { - var lookahead = this._string[this._currentIndex]; - return this._pathSegTypeFromChar(lookahead); - } - - Source.prototype._pathSegTypeFromChar = function(lookahead) { - switch (lookahead) { - case "Z": - case "z": - return SVGPathSeg.PATHSEG_CLOSEPATH; - case "M": - return SVGPathSeg.PATHSEG_MOVETO_ABS; - case "m": - return SVGPathSeg.PATHSEG_MOVETO_REL; - case "L": - return SVGPathSeg.PATHSEG_LINETO_ABS; - case "l": - return SVGPathSeg.PATHSEG_LINETO_REL; - case "C": - return SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS; - case "c": - return SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL; - case "Q": - return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS; - case "q": - return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL; - case "A": - return SVGPathSeg.PATHSEG_ARC_ABS; - case "a": - return SVGPathSeg.PATHSEG_ARC_REL; - case "H": - return SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS; - case "h": - return SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL; - case "V": - return SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS; - case "v": - return SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL; - case "S": - return SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; - case "s": - return SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL; - case "T": - return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; - case "t": - return SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; - default: - return SVGPathSeg.PATHSEG_UNKNOWN; - } - } - - Source.prototype._nextCommandHelper = function(lookahead, previousCommand) { - // Check for remaining coordinates in the current command. - if ((lookahead == "+" || lookahead == "-" || lookahead == "." || (lookahead >= "0" && lookahead <= "9")) && previousCommand != SVGPathSeg.PATHSEG_CLOSEPATH) { - if (previousCommand == SVGPathSeg.PATHSEG_MOVETO_ABS) - return SVGPathSeg.PATHSEG_LINETO_ABS; - if (previousCommand == SVGPathSeg.PATHSEG_MOVETO_REL) - return SVGPathSeg.PATHSEG_LINETO_REL; - return previousCommand; - } - return SVGPathSeg.PATHSEG_UNKNOWN; - } - - Source.prototype.initialCommandIsMoveTo = function() { - // If the path is empty it is still valid, so return true. - if (!this.hasMoreData()) - return true; - var command = this.peekSegmentType(); - // Path must start with moveTo. - return command == SVGPathSeg.PATHSEG_MOVETO_ABS || command == SVGPathSeg.PATHSEG_MOVETO_REL; - } - - // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. - // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF - Source.prototype._parseNumber = function() { - var exponent = 0; - var integer = 0; - var frac = 1; - var decimal = 0; - var sign = 1; - var expsign = 1; - - var startIndex = this._currentIndex; - - this._skipOptionalSpaces(); - - // Read the sign. - if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "+") - this._currentIndex++; - else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "-") { - this._currentIndex++; - sign = -1; - } - - if (this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") && this._string.charAt(this._currentIndex) != ".")) - // The first character of a number must be one of [0-9+-.]. - return undefined; - - // Read the integer part, build right-to-left. - var startIntPartIndex = this._currentIndex; - while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") - this._currentIndex++; // Advance to first non-digit. - - if (this._currentIndex != startIntPartIndex) { - var scanIntPartIndex = this._currentIndex - 1; - var multiplier = 1; - while (scanIntPartIndex >= startIntPartIndex) { - integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0"); - multiplier *= 10; - } - } - - // Read the decimals. - if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") { - this._currentIndex++; - - // There must be a least one digit following the . - if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") - return undefined; - while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") - decimal += (this._string.charAt(this._currentIndex++) - "0") * (frac *= 0.1); - } - - // Read the exponent part. - if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == "e" || this._string.charAt(this._currentIndex) == "E") && (this._string.charAt(this._currentIndex + 1) != "x" && this._string.charAt(this._currentIndex + 1) != "m")) { - this._currentIndex++; - - // Read the sign of the exponent. - if (this._string.charAt(this._currentIndex) == "+") { - this._currentIndex++; - } else if (this._string.charAt(this._currentIndex) == "-") { - this._currentIndex++; - expsign = -1; - } - - // There must be an exponent. - if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") - return undefined; - - while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") { - exponent *= 10; - exponent += (this._string.charAt(this._currentIndex) - "0"); - this._currentIndex++; - } - } - - var number = integer + decimal; - number *= sign; - - if (exponent) - number *= Math.pow(10, expsign * exponent); - - if (startIndex == this._currentIndex) - return undefined; - - this._skipOptionalSpacesOrDelimiter(); - - return number; - } - - Source.prototype._parseArcFlag = function() { - if (this._currentIndex >= this._endIndex) - return undefined; - var flag = false; - var flagChar = this._string.charAt(this._currentIndex++); - if (flagChar == "0") - flag = false; - else if (flagChar == "1") - flag = true; - else - return undefined; - - this._skipOptionalSpacesOrDelimiter(); - return flag; - } - - Source.prototype.parseSegment = function() { - var lookahead = this._string[this._currentIndex]; - var command = this._pathSegTypeFromChar(lookahead); - if (command == SVGPathSeg.PATHSEG_UNKNOWN) { - // Possibly an implicit command. Not allowed if this is the first command. - if (this._previousCommand == SVGPathSeg.PATHSEG_UNKNOWN) - return null; - command = this._nextCommandHelper(lookahead, this._previousCommand); - if (command == SVGPathSeg.PATHSEG_UNKNOWN) - return null; - } else { - this._currentIndex++; - } - - this._previousCommand = command; - - switch (command) { - case SVGPathSeg.PATHSEG_MOVETO_REL: - return new SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); - case SVGPathSeg.PATHSEG_MOVETO_ABS: - return new SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); - case SVGPathSeg.PATHSEG_LINETO_REL: - return new SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); - case SVGPathSeg.PATHSEG_LINETO_ABS: - return new SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); - case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: - return new SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber()); - case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: - return new SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber()); - case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: - return new SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber()); - case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: - return new SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber()); - case SVGPathSeg.PATHSEG_CLOSEPATH: - this._skipOptionalSpaces(); - return new SVGPathSegClosePath(owningPathSegList); - case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); - case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); - case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: - var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2); - case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: - var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2); - case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1); - case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()}; - return new SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1); - case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: - return new SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber()); - case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: - return new SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); - case SVGPathSeg.PATHSEG_ARC_REL: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}; - return new SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); - case SVGPathSeg.PATHSEG_ARC_ABS: - var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()}; - return new SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); - default: - throw "Unknown path seg type." - } - } - - var builder = new Builder(); - var source = new Source(string); - - if (!source.initialCommandIsMoveTo()) - return []; - while (source.hasMoreData()) { - var pathSeg = source.parseSegment(); - if (!pathSeg) - return []; - builder.appendSegment(pathSeg); - } - - return builder.pathSegList; - } - } - }()); - - /* jshint ignore:end */ - - if (typeof define === 'function' && define.amd) { - define("c3", ["d3"], function () { return c3; }); - } else if ('undefined' !== typeof exports && 'undefined' !== typeof module) { - module.exports = c3; - } else { - window.c3 = c3; - } - -})(window); diff --git a/debian/missing-sources/c3-0.4.18.css b/debian/missing-sources/c3-0.4.18.css new file mode 100644 index 000000000..89969eeae --- /dev/null +++ b/debian/missing-sources/c3-0.4.18.css @@ -0,0 +1,174 @@ +/*-- Chart --*/ +.c3 svg { + font: 10px sans-serif; + -webkit-tap-highlight-color: transparent; } + +.c3 path, .c3 line { + fill: none; + stroke: #000; } + +.c3 text { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; } + +.c3-legend-item-tile, +.c3-xgrid-focus, +.c3-ygrid, +.c3-event-rect, +.c3-bars path { + shape-rendering: crispEdges; } + +.c3-chart-arc path { + stroke: #fff; } + +.c3-chart-arc text { + fill: #fff; + font-size: 13px; } + +/*-- Axis --*/ +/*-- Grid --*/ +.c3-grid line { + stroke: #aaa; } + +.c3-grid text { + fill: #aaa; } + +.c3-xgrid, .c3-ygrid { + stroke-dasharray: 3 3; } + +/*-- Text on Chart --*/ +.c3-text.c3-empty { + fill: #808080; + font-size: 2em; } + +/*-- Line --*/ +.c3-line { + stroke-width: 1px; } + +/*-- Point --*/ +.c3-circle._expanded_ { + stroke-width: 1px; + stroke: white; } + +.c3-selected-circle { + fill: white; + stroke-width: 2px; } + +/*-- Bar --*/ +.c3-bar { + stroke-width: 0; } + +.c3-bar._expanded_ { + fill-opacity: 1; + fill-opacity: 0.75; } + +/*-- Focus --*/ +.c3-target.c3-focused { + opacity: 1; } + +.c3-target.c3-focused path.c3-line, .c3-target.c3-focused path.c3-step { + stroke-width: 2px; } + +.c3-target.c3-defocused { + opacity: 0.3 !important; } + +/*-- Region --*/ +.c3-region { + fill: steelblue; + fill-opacity: .1; } + +/*-- Brush --*/ +.c3-brush .extent { + fill-opacity: .1; } + +/*-- Select - Drag --*/ +/*-- Legend --*/ +.c3-legend-item { + font-size: 12px; } + +.c3-legend-item-hidden { + opacity: 0.15; } + +.c3-legend-background { + opacity: 0.75; + fill: white; + stroke: lightgray; + stroke-width: 1; } + +/*-- Title --*/ +.c3-title { + font: 14px sans-serif; } + +/*-- Tooltip --*/ +.c3-tooltip-container { + z-index: 10; } + +.c3-tooltip { + border-collapse: collapse; + border-spacing: 0; + background-color: #fff; + empty-cells: show; + -webkit-box-shadow: 7px 7px 12px -9px #777777; + -moz-box-shadow: 7px 7px 12px -9px #777777; + box-shadow: 7px 7px 12px -9px #777777; + opacity: 0.9; } + +.c3-tooltip tr { + border: 1px solid #CCC; } + +.c3-tooltip th { + background-color: #aaa; + font-size: 14px; + padding: 2px 5px; + text-align: left; + color: #FFF; } + +.c3-tooltip td { + font-size: 13px; + padding: 3px 6px; + background-color: #fff; + border-left: 1px dotted #999; } + +.c3-tooltip td > span { + display: inline-block; + width: 10px; + height: 10px; + margin-right: 6px; } + +.c3-tooltip td.value { + text-align: right; } + +/*-- Area --*/ +.c3-area { + stroke-width: 0; + opacity: 0.2; } + +/*-- Arc --*/ +.c3-chart-arcs-title { + dominant-baseline: middle; + font-size: 1.3em; } + +.c3-chart-arcs .c3-chart-arcs-background { + fill: #e0e0e0; + stroke: none; } + +.c3-chart-arcs .c3-chart-arcs-gauge-unit { + fill: #000; + font-size: 16px; } + +.c3-chart-arcs .c3-chart-arcs-gauge-max { + fill: #777; } + +.c3-chart-arcs .c3-chart-arcs-gauge-min { + fill: #777; } + +.c3-chart-arc .c3-gauge-value { + fill: #000; + /* font-size: 28px !important;*/ } + +.c3-chart-arc.c3-target g path { + opacity: 1; } + +.c3-chart-arc.c3-target.c3-focused g path { + opacity: 1; } diff --git a/debian/missing-sources/c3-0.4.18.js b/debian/missing-sources/c3-0.4.18.js new file mode 100644 index 000000000..d607b1772 --- /dev/null +++ b/debian/missing-sources/c3-0.4.18.js @@ -0,0 +1,9236 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.c3 = factory()); +}(this, (function () { 'use strict'; + +var CLASS = { + target: 'c3-target', + chart: 'c3-chart', + chartLine: 'c3-chart-line', + chartLines: 'c3-chart-lines', + chartBar: 'c3-chart-bar', + chartBars: 'c3-chart-bars', + chartText: 'c3-chart-text', + chartTexts: 'c3-chart-texts', + chartArc: 'c3-chart-arc', + chartArcs: 'c3-chart-arcs', + chartArcsTitle: 'c3-chart-arcs-title', + chartArcsBackground: 'c3-chart-arcs-background', + chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit', + chartArcsGaugeMax: 'c3-chart-arcs-gauge-max', + chartArcsGaugeMin: 'c3-chart-arcs-gauge-min', + selectedCircle: 'c3-selected-circle', + selectedCircles: 'c3-selected-circles', + eventRect: 'c3-event-rect', + eventRects: 'c3-event-rects', + eventRectsSingle: 'c3-event-rects-single', + eventRectsMultiple: 'c3-event-rects-multiple', + zoomRect: 'c3-zoom-rect', + brush: 'c3-brush', + focused: 'c3-focused', + defocused: 'c3-defocused', + region: 'c3-region', + regions: 'c3-regions', + title: 'c3-title', + tooltipContainer: 'c3-tooltip-container', + tooltip: 'c3-tooltip', + tooltipName: 'c3-tooltip-name', + shape: 'c3-shape', + shapes: 'c3-shapes', + line: 'c3-line', + lines: 'c3-lines', + bar: 'c3-bar', + bars: 'c3-bars', + circle: 'c3-circle', + circles: 'c3-circles', + arc: 'c3-arc', + arcs: 'c3-arcs', + area: 'c3-area', + areas: 'c3-areas', + empty: 'c3-empty', + text: 'c3-text', + texts: 'c3-texts', + gaugeValue: 'c3-gauge-value', + grid: 'c3-grid', + gridLines: 'c3-grid-lines', + xgrid: 'c3-xgrid', + xgrids: 'c3-xgrids', + xgridLine: 'c3-xgrid-line', + xgridLines: 'c3-xgrid-lines', + xgridFocus: 'c3-xgrid-focus', + ygrid: 'c3-ygrid', + ygrids: 'c3-ygrids', + ygridLine: 'c3-ygrid-line', + ygridLines: 'c3-ygrid-lines', + axis: 'c3-axis', + axisX: 'c3-axis-x', + axisXLabel: 'c3-axis-x-label', + axisY: 'c3-axis-y', + axisYLabel: 'c3-axis-y-label', + axisY2: 'c3-axis-y2', + axisY2Label: 'c3-axis-y2-label', + legendBackground: 'c3-legend-background', + legendItem: 'c3-legend-item', + legendItemEvent: 'c3-legend-item-event', + legendItemTile: 'c3-legend-item-tile', + legendItemHidden: 'c3-legend-item-hidden', + legendItemFocused: 'c3-legend-item-focused', + dragarea: 'c3-dragarea', + EXPANDED: '_expanded_', + SELECTED: '_selected_', + INCLUDED: '_included_' +}; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; +} : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; +}; + + + + + + + + + + + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + + + + + + + + + + + +var inherits = function (subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; +}; + + + + + + + + + + + +var possibleConstructorReturn = function (self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; +}; + +var isValue = function isValue(v) { + return v || v === 0; +}; +var isFunction = function isFunction(o) { + return typeof o === 'function'; +}; +var isArray = function isArray(o) { + return Array.isArray(o); +}; +var isString = function isString(o) { + return typeof o === 'string'; +}; +var isUndefined = function isUndefined(v) { + return typeof v === 'undefined'; +}; +var isDefined = function isDefined(v) { + return typeof v !== 'undefined'; +}; +var ceil10 = function ceil10(v) { + return Math.ceil(v / 10) * 10; +}; +var asHalfPixel = function asHalfPixel(n) { + return Math.ceil(n) + 0.5; +}; +var diffDomain = function diffDomain(d) { + return d[1] - d[0]; +}; +var isEmpty = function isEmpty(o) { + return typeof o === 'undefined' || o === null || isString(o) && o.length === 0 || (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === 'object' && Object.keys(o).length === 0; +}; +var notEmpty = function notEmpty(o) { + return !c3_chart_internal_fn.isEmpty(o); +}; +var getOption = function getOption(options, key, defaultValue) { + return isDefined(options[key]) ? options[key] : defaultValue; +}; +var hasValue = function hasValue(dict, value) { + var found = false; + Object.keys(dict).forEach(function (key) { + if (dict[key] === value) { + found = true; + } + }); + return found; +}; +var sanitise = function sanitise(str) { + return typeof str === 'string' ? str.replace(//g, '>') : str; +}; +var getPathBox = function getPathBox(path) { + var box = path.getBoundingClientRect(), + items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)], + minX = items[0].x, + minY = Math.min(items[0].y, items[1].y); + return { x: minX, y: minY, width: box.width, height: box.height }; +}; + +var c3_axis_fn; +var c3_axis_internal_fn; + +function AxisInternal(component, params) { + var internal = this; + internal.component = component; + internal.params = params || {}; + + internal.d3 = component.d3; + internal.scale = internal.d3.scale.linear(); + internal.range; + internal.orient = "bottom"; + internal.innerTickSize = 6; + internal.outerTickSize = this.params.withOuterTick ? 6 : 0; + internal.tickPadding = 3; + internal.tickValues = null; + internal.tickFormat; + internal.tickArguments; + + internal.tickOffset = 0; + internal.tickCulling = true; + internal.tickCentered; + internal.tickTextCharSize; + internal.tickTextRotate = internal.params.tickTextRotate; + internal.tickLength; + + internal.axis = internal.generateAxis(); +} +c3_axis_internal_fn = AxisInternal.prototype; + +c3_axis_internal_fn.axisX = function (selection, x, tickOffset) { + selection.attr("transform", function (d) { + return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)"; + }); +}; +c3_axis_internal_fn.axisY = function (selection, y) { + selection.attr("transform", function (d) { + return "translate(0," + Math.ceil(y(d)) + ")"; + }); +}; +c3_axis_internal_fn.scaleExtent = function (domain) { + var start = domain[0], + stop = domain[domain.length - 1]; + return start < stop ? [start, stop] : [stop, start]; +}; +c3_axis_internal_fn.generateTicks = function (scale) { + var internal = this; + var i, + domain, + ticks = []; + if (scale.ticks) { + return scale.ticks.apply(scale, internal.tickArguments); + } + domain = scale.domain(); + for (i = Math.ceil(domain[0]); i < domain[1]; i++) { + ticks.push(i); + } + if (ticks.length > 0 && ticks[0] > 0) { + ticks.unshift(ticks[0] - (ticks[1] - ticks[0])); + } + return ticks; +}; +c3_axis_internal_fn.copyScale = function () { + var internal = this; + var newScale = internal.scale.copy(), + domain; + if (internal.params.isCategory) { + domain = internal.scale.domain(); + newScale.domain([domain[0], domain[1] - 1]); + } + return newScale; +}; +c3_axis_internal_fn.textFormatted = function (v) { + var internal = this, + formatted = internal.tickFormat ? internal.tickFormat(v) : v; + return typeof formatted !== 'undefined' ? formatted : ''; +}; +c3_axis_internal_fn.updateRange = function () { + var internal = this; + internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range()); + return internal.range; +}; +c3_axis_internal_fn.updateTickTextCharSize = function (tick) { + var internal = this; + if (internal.tickTextCharSize) { + return internal.tickTextCharSize; + } + var size = { + h: 11.5, + w: 5.5 + }; + tick.select('text').text(function (d) { + return internal.textFormatted(d); + }).each(function (d) { + var box = this.getBoundingClientRect(), + text = internal.textFormatted(d), + h = box.height, + w = text ? box.width / text.length : undefined; + if (h && w) { + size.h = h; + size.w = w; + } + }).text(''); + internal.tickTextCharSize = size; + return size; +}; +c3_axis_internal_fn.transitionise = function (selection) { + return this.params.withoutTransition ? selection : this.d3.transition(selection); +}; +c3_axis_internal_fn.isVertical = function () { + return this.orient === 'left' || this.orient === 'right'; +}; +c3_axis_internal_fn.tspanData = function (d, i, ticks, scale) { + var internal = this; + var splitted = internal.params.tickMultiline ? internal.splitTickText(d, ticks, scale) : [].concat(internal.textFormatted(d)); + return splitted.map(function (s) { + return { index: i, splitted: s, length: splitted.length }; + }); +}; +c3_axis_internal_fn.splitTickText = function (d, ticks, scale) { + var internal = this, + tickText = internal.textFormatted(d), + maxWidth = internal.params.tickWidth, + subtext, + spaceIndex, + textWidth, + splitted = []; + + if (Object.prototype.toString.call(tickText) === "[object Array]") { + return tickText; + } + + if (!maxWidth || maxWidth <= 0) { + maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(ticks[1]) - scale(ticks[0])) - 12 : 110; + } + + function split(splitted, text) { + spaceIndex = undefined; + for (var i = 1; i < text.length; i++) { + if (text.charAt(i) === ' ') { + spaceIndex = i; + } + subtext = text.substr(0, i + 1); + textWidth = internal.tickTextCharSize.w * subtext.length; + // if text width gets over tick width, split by space index or crrent index + if (maxWidth < textWidth) { + return split(splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), text.slice(spaceIndex ? spaceIndex + 1 : i)); + } + } + return splitted.concat(text); + } + + return split(splitted, tickText + ""); +}; +c3_axis_internal_fn.updateTickLength = function () { + var internal = this; + internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding; +}; +c3_axis_internal_fn.lineY2 = function (d) { + var internal = this, + tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset); + return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0; +}; +c3_axis_internal_fn.textY = function () { + var internal = this, + rotate = internal.tickTextRotate; + return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength; +}; +c3_axis_internal_fn.textTransform = function () { + var internal = this, + rotate = internal.tickTextRotate; + return rotate ? "rotate(" + rotate + ")" : ""; +}; +c3_axis_internal_fn.textTextAnchor = function () { + var internal = this, + rotate = internal.tickTextRotate; + return rotate ? rotate > 0 ? "start" : "end" : "middle"; +}; +c3_axis_internal_fn.tspanDx = function () { + var internal = this, + rotate = internal.tickTextRotate; + return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0; +}; +c3_axis_internal_fn.tspanDy = function (d, i) { + var internal = this, + dy = internal.tickTextCharSize.h; + if (i === 0) { + if (internal.isVertical()) { + dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3); + } else { + dy = ".71em"; + } + } + return dy; +}; + +c3_axis_internal_fn.generateAxis = function () { + var internal = this, + d3 = internal.d3, + params = internal.params; + function axis(g) { + g.each(function () { + var g = axis.g = d3.select(this); + + var scale0 = this.__chart__ || internal.scale, + scale1 = this.__chart__ = internal.copyScale(); + + var ticks = internal.tickValues ? internal.tickValues : internal.generateTicks(scale1), + tick = g.selectAll(".tick").data(ticks, scale1), + tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6), + + // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks. + tickExit = tick.exit().remove(), + tickUpdate = internal.transitionise(tick).style("opacity", 1), + tickTransform, + tickX, + tickY; + + if (params.isCategory) { + internal.tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2); + tickX = internal.tickCentered ? 0 : internal.tickOffset; + tickY = internal.tickCentered ? internal.tickOffset : 0; + } else { + internal.tickOffset = tickX = 0; + } + + tickEnter.append("line"); + tickEnter.append("text"); + + internal.updateRange(); + internal.updateTickLength(); + internal.updateTickTextCharSize(g.select('.tick')); + + var lineUpdate = tickUpdate.select("line"), + textUpdate = tickUpdate.select("text"), + tspanUpdate = tick.select("text").selectAll('tspan').data(function (d, i) { + return internal.tspanData(d, i, ticks, scale1); + }); + + tspanUpdate.enter().append('tspan'); + tspanUpdate.exit().remove(); + tspanUpdate.text(function (d) { + return d.splitted; + }); + + var path = g.selectAll(".domain").data([0]), + pathUpdate = (path.enter().append("path").attr("class", "domain"), internal.transitionise(path)); + + // TODO: each attr should be one function and change its behavior by internal.orient, probably + switch (internal.orient) { + case "bottom": + { + tickTransform = internal.axisX; + lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", function (d, i) { + return internal.lineY2(d, i); + }); + textUpdate.attr("x", 0).attr("y", function (d, i) { + return internal.textY(d, i); + }).attr("transform", function (d, i) { + return internal.textTransform(d, i); + }).style("text-anchor", function (d, i) { + return internal.textTextAnchor(d, i); + }); + tspanUpdate.attr('x', 0).attr("dy", function (d, i) { + return internal.tspanDy(d, i); + }).attr('dx', function (d, i) { + return internal.tspanDx(d, i); + }); + pathUpdate.attr("d", "M" + internal.range[0] + "," + internal.outerTickSize + "V0H" + internal.range[1] + "V" + internal.outerTickSize); + break; + } + case "top": + { + // TODO: rotated tick text + tickTransform = internal.axisX; + lineUpdate.attr("x2", 0).attr("y2", -internal.innerTickSize); + textUpdate.attr("x", 0).attr("y", -internal.tickLength).style("text-anchor", "middle"); + tspanUpdate.attr('x', 0).attr("dy", "0em"); + pathUpdate.attr("d", "M" + internal.range[0] + "," + -internal.outerTickSize + "V0H" + internal.range[1] + "V" + -internal.outerTickSize); + break; + } + case "left": + { + tickTransform = internal.axisY; + lineUpdate.attr("x2", -internal.innerTickSize).attr("y1", tickY).attr("y2", tickY); + textUpdate.attr("x", -internal.tickLength).attr("y", internal.tickOffset).style("text-anchor", "end"); + tspanUpdate.attr('x', -internal.tickLength).attr("dy", function (d, i) { + return internal.tspanDy(d, i); + }); + pathUpdate.attr("d", "M" + -internal.outerTickSize + "," + internal.range[0] + "H0V" + internal.range[1] + "H" + -internal.outerTickSize); + break; + } + case "right": + { + tickTransform = internal.axisY; + lineUpdate.attr("x2", internal.innerTickSize).attr("y2", 0); + textUpdate.attr("x", internal.tickLength).attr("y", 0).style("text-anchor", "start"); + tspanUpdate.attr('x', internal.tickLength).attr("dy", function (d, i) { + return internal.tspanDy(d, i); + }); + pathUpdate.attr("d", "M" + internal.outerTickSize + "," + internal.range[0] + "H0V" + internal.range[1] + "H" + internal.outerTickSize); + break; + } + } + if (scale1.rangeBand) { + var x = scale1, + dx = x.rangeBand() / 2; + scale0 = scale1 = function scale1(d) { + return x(d) + dx; + }; + } else if (scale0.rangeBand) { + scale0 = scale1; + } else { + tickExit.call(tickTransform, scale1, internal.tickOffset); + } + tickEnter.call(tickTransform, scale0, internal.tickOffset); + tickUpdate.call(tickTransform, scale1, internal.tickOffset); + }); + } + axis.scale = function (x) { + if (!arguments.length) { + return internal.scale; + } + internal.scale = x; + return axis; + }; + axis.orient = function (x) { + if (!arguments.length) { + return internal.orient; + } + internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + "" : "bottom"; + return axis; + }; + axis.tickFormat = function (format) { + if (!arguments.length) { + return internal.tickFormat; + } + internal.tickFormat = format; + return axis; + }; + axis.tickCentered = function (isCentered) { + if (!arguments.length) { + return internal.tickCentered; + } + internal.tickCentered = isCentered; + return axis; + }; + axis.tickOffset = function () { + return internal.tickOffset; + }; + axis.tickInterval = function () { + var interval, length; + if (params.isCategory) { + interval = internal.tickOffset * 2; + } else { + length = axis.g.select('path.domain').node().getTotalLength() - internal.outerTickSize * 2; + interval = length / axis.g.selectAll('line').size(); + } + return interval === Infinity ? 0 : interval; + }; + axis.ticks = function () { + if (!arguments.length) { + return internal.tickArguments; + } + internal.tickArguments = arguments; + return axis; + }; + axis.tickCulling = function (culling) { + if (!arguments.length) { + return internal.tickCulling; + } + internal.tickCulling = culling; + return axis; + }; + axis.tickValues = function (x) { + if (typeof x === 'function') { + internal.tickValues = function () { + return x(internal.scale.domain()); + }; + } else { + if (!arguments.length) { + return internal.tickValues; + } + internal.tickValues = x; + } + return axis; + }; + return axis; +}; + +var Axis = function (_Component) { + inherits(Axis, _Component); + + function Axis(owner) { + classCallCheck(this, Axis); + + var fn = { + fn: c3_axis_fn, + internal: { + fn: c3_axis_internal_fn + } + }; + + var _this = possibleConstructorReturn(this, (Axis.__proto__ || Object.getPrototypeOf(Axis)).call(this, owner, 'axis', fn)); + + _this.d3 = owner.d3; + _this.internal = AxisInternal; + return _this; + } + + return Axis; +}(Component); + +c3_axis_fn = Axis.prototype; + +c3_axis_fn.init = function init() { + var $$ = this.owner, + config = $$.config, + main = $$.main; + $$.axes.x = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisX).attr("clip-path", $$.clipPathForXAxis).attr("transform", $$.getTranslate('x')).style("visibility", config.axis_x_show ? 'visible' : 'hidden'); + $$.axes.x.append("text").attr("class", CLASS.axisXLabel).attr("transform", config.axis_rotated ? "rotate(-90)" : "").style("text-anchor", this.textAnchorForXAxisLabel.bind(this)); + $$.axes.y = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisY).attr("clip-path", config.axis_y_inner ? "" : $$.clipPathForYAxis).attr("transform", $$.getTranslate('y')).style("visibility", config.axis_y_show ? 'visible' : 'hidden'); + $$.axes.y.append("text").attr("class", CLASS.axisYLabel).attr("transform", config.axis_rotated ? "" : "rotate(-90)").style("text-anchor", this.textAnchorForYAxisLabel.bind(this)); + + $$.axes.y2 = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisY2 + // clip-path? + ).attr("transform", $$.getTranslate('y2')).style("visibility", config.axis_y2_show ? 'visible' : 'hidden'); + $$.axes.y2.append("text").attr("class", CLASS.axisY2Label).attr("transform", config.axis_rotated ? "" : "rotate(-90)").style("text-anchor", this.textAnchorForY2AxisLabel.bind(this)); +}; +c3_axis_fn.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { + var $$ = this.owner, + config = $$.config, + axisParams = { + isCategory: $$.isCategorized(), + withOuterTick: withOuterTick, + tickMultiline: config.axis_x_tick_multiline, + tickWidth: config.axis_x_tick_width, + tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate, + withoutTransition: withoutTransition + }, + axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient); + + if ($$.isTimeSeries() && tickValues && typeof tickValues !== "function") { + tickValues = tickValues.map(function (v) { + return $$.parseDate(v); + }); + } + + // Set tick + axis.tickFormat(tickFormat).tickValues(tickValues); + if ($$.isCategorized()) { + axis.tickCentered(config.axis_x_tick_centered); + if (isEmpty(config.axis_x_tick_culling)) { + config.axis_x_tick_culling = false; + } + } + + return axis; +}; +c3_axis_fn.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) { + var $$ = this.owner, + config = $$.config, + tickValues; + if (config.axis_x_tick_fit || config.axis_x_tick_count) { + tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries()); + } + if (axis) { + axis.tickValues(tickValues); + } else { + $$.xAxis.tickValues(tickValues); + $$.subXAxis.tickValues(tickValues); + } + return tickValues; +}; +c3_axis_fn.getYAxis = function getYAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { + var $$ = this.owner, + config = $$.config, + axisParams = { + withOuterTick: withOuterTick, + withoutTransition: withoutTransition, + tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate + }, + axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient).tickFormat(tickFormat); + if ($$.isTimeSeriesY()) { + axis.ticks($$.d3.time[config.axis_y_tick_time_value], config.axis_y_tick_time_interval); + } else { + axis.tickValues(tickValues); + } + return axis; +}; +c3_axis_fn.getId = function getId(id) { + var config = this.owner.config; + return id in config.data_axes ? config.data_axes[id] : 'y'; +}; +c3_axis_fn.getXAxisTickFormat = function getXAxisTickFormat() { + var $$ = this.owner, + config = $$.config, + format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { + return v < 0 ? v.toFixed(0) : v; + }; + if (config.axis_x_tick_format) { + if (isFunction(config.axis_x_tick_format)) { + format = config.axis_x_tick_format; + } else if ($$.isTimeSeries()) { + format = function format(date) { + return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : ""; + }; + } + } + return isFunction(format) ? function (v) { + return format.call($$, v); + } : format; +}; +c3_axis_fn.getTickValues = function getTickValues(tickValues, axis) { + return tickValues ? tickValues : axis ? axis.tickValues() : undefined; +}; +c3_axis_fn.getXAxisTickValues = function getXAxisTickValues() { + return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis); +}; +c3_axis_fn.getYAxisTickValues = function getYAxisTickValues() { + return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis); +}; +c3_axis_fn.getY2AxisTickValues = function getY2AxisTickValues() { + return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis); +}; +c3_axis_fn.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) { + var $$ = this.owner, + config = $$.config, + option; + if (axisId === 'y') { + option = config.axis_y_label; + } else if (axisId === 'y2') { + option = config.axis_y2_label; + } else if (axisId === 'x') { + option = config.axis_x_label; + } + return option; +}; +c3_axis_fn.getLabelText = function getLabelText(axisId) { + var option = this.getLabelOptionByAxisId(axisId); + return isString(option) ? option : option ? option.text : null; +}; +c3_axis_fn.setLabelText = function setLabelText(axisId, text) { + var $$ = this.owner, + config = $$.config, + option = this.getLabelOptionByAxisId(axisId); + if (isString(option)) { + if (axisId === 'y') { + config.axis_y_label = text; + } else if (axisId === 'y2') { + config.axis_y2_label = text; + } else if (axisId === 'x') { + config.axis_x_label = text; + } + } else if (option) { + option.text = text; + } +}; +c3_axis_fn.getLabelPosition = function getLabelPosition(axisId, defaultPosition) { + var option = this.getLabelOptionByAxisId(axisId), + position = option && (typeof option === 'undefined' ? 'undefined' : _typeof(option)) === 'object' && option.position ? option.position : defaultPosition; + return { + isInner: position.indexOf('inner') >= 0, + isOuter: position.indexOf('outer') >= 0, + isLeft: position.indexOf('left') >= 0, + isCenter: position.indexOf('center') >= 0, + isRight: position.indexOf('right') >= 0, + isTop: position.indexOf('top') >= 0, + isMiddle: position.indexOf('middle') >= 0, + isBottom: position.indexOf('bottom') >= 0 + }; +}; +c3_axis_fn.getXAxisLabelPosition = function getXAxisLabelPosition() { + return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right'); +}; +c3_axis_fn.getYAxisLabelPosition = function getYAxisLabelPosition() { + return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); +}; +c3_axis_fn.getY2AxisLabelPosition = function getY2AxisLabelPosition() { + return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); +}; +c3_axis_fn.getLabelPositionById = function getLabelPositionById(id) { + return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition(); +}; +c3_axis_fn.textForXAxisLabel = function textForXAxisLabel() { + return this.getLabelText('x'); +}; +c3_axis_fn.textForYAxisLabel = function textForYAxisLabel() { + return this.getLabelText('y'); +}; +c3_axis_fn.textForY2AxisLabel = function textForY2AxisLabel() { + return this.getLabelText('y2'); +}; +c3_axis_fn.xForAxisLabel = function xForAxisLabel(forHorizontal, position) { + var $$ = this.owner; + if (forHorizontal) { + return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width; + } else { + return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0; + } +}; +c3_axis_fn.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) { + if (forHorizontal) { + return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0"; + } else { + return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0"; + } +}; +c3_axis_fn.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) { + if (forHorizontal) { + return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end'; + } else { + return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end'; + } +}; +c3_axis_fn.xForXAxisLabel = function xForXAxisLabel() { + return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); +}; +c3_axis_fn.xForYAxisLabel = function xForYAxisLabel() { + return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); +}; +c3_axis_fn.xForY2AxisLabel = function xForY2AxisLabel() { + return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); +}; +c3_axis_fn.dxForXAxisLabel = function dxForXAxisLabel() { + return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); +}; +c3_axis_fn.dxForYAxisLabel = function dxForYAxisLabel() { + return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); +}; +c3_axis_fn.dxForY2AxisLabel = function dxForY2AxisLabel() { + return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); +}; +c3_axis_fn.dyForXAxisLabel = function dyForXAxisLabel() { + var $$ = this.owner, + config = $$.config, + position = this.getXAxisLabelPosition(); + if (config.axis_rotated) { + return position.isInner ? "1.2em" : -25 - this.getMaxTickWidth('x'); + } else { + return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em"; + } +}; +c3_axis_fn.dyForYAxisLabel = function dyForYAxisLabel() { + var $$ = this.owner, + position = this.getYAxisLabelPosition(); + if ($$.config.axis_rotated) { + return position.isInner ? "-0.5em" : "3em"; + } else { + return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10); + } +}; +c3_axis_fn.dyForY2AxisLabel = function dyForY2AxisLabel() { + var $$ = this.owner, + position = this.getY2AxisLabelPosition(); + if ($$.config.axis_rotated) { + return position.isInner ? "1.2em" : "-2.2em"; + } else { + return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15); + } +}; +c3_axis_fn.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() { + var $$ = this.owner; + return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition()); +}; +c3_axis_fn.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() { + var $$ = this.owner; + return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition()); +}; +c3_axis_fn.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() { + var $$ = this.owner; + return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition()); +}; +c3_axis_fn.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) { + var $$ = this.owner, + config = $$.config, + maxWidth = 0, + targetsToShow, + scale, + axis, + dummy, + svg; + if (withoutRecompute && $$.currentMaxTickWidths[id]) { + return $$.currentMaxTickWidths[id]; + } + if ($$.svg) { + targetsToShow = $$.filterTargetsToShow($$.data.targets); + if (id === 'y') { + scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')); + axis = this.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, false, true, true); + } else if (id === 'y2') { + scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')); + axis = this.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, false, true, true); + } else { + scale = $$.x.copy().domain($$.getXDomain(targetsToShow)); + axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true); + this.updateXAxisTickValues(targetsToShow, axis); + } + dummy = $$.d3.select('body').append('div').classed('c3', true); + svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0), svg.append('g').call(axis).each(function () { + $$.d3.select(this).selectAll('text').each(function () { + var box = this.getBoundingClientRect(); + if (maxWidth < box.width) { + maxWidth = box.width; + } + }); + dummy.remove(); + }); + } + $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth; + return $$.currentMaxTickWidths[id]; +}; + +c3_axis_fn.updateLabels = function updateLabels(withTransition) { + var $$ = this.owner; + var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), + axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), + axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label); + (withTransition ? axisXLabel.transition() : axisXLabel).attr("x", this.xForXAxisLabel.bind(this)).attr("dx", this.dxForXAxisLabel.bind(this)).attr("dy", this.dyForXAxisLabel.bind(this)).text(this.textForXAxisLabel.bind(this)); + (withTransition ? axisYLabel.transition() : axisYLabel).attr("x", this.xForYAxisLabel.bind(this)).attr("dx", this.dxForYAxisLabel.bind(this)).attr("dy", this.dyForYAxisLabel.bind(this)).text(this.textForYAxisLabel.bind(this)); + (withTransition ? axisY2Label.transition() : axisY2Label).attr("x", this.xForY2AxisLabel.bind(this)).attr("dx", this.dxForY2AxisLabel.bind(this)).attr("dy", this.dyForY2AxisLabel.bind(this)).text(this.textForY2AxisLabel.bind(this)); +}; +c3_axis_fn.getPadding = function getPadding(padding, key, defaultValue, domainLength) { + var p = typeof padding === 'number' ? padding : padding[key]; + if (!isValue(p)) { + return defaultValue; + } + if (padding.unit === 'ratio') { + return padding[key] * domainLength; + } + // assume padding is pixels if unit is not specified + return this.convertPixelsToAxisPadding(p, domainLength); +}; +c3_axis_fn.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) { + var $$ = this.owner, + length = $$.config.axis_rotated ? $$.width : $$.height; + return domainLength * (pixels / length); +}; +c3_axis_fn.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) { + var tickValues = values, + targetCount, + start, + end, + count, + interval, + i, + tickValue; + if (tickCount) { + targetCount = isFunction(tickCount) ? tickCount() : tickCount; + // compute ticks according to tickCount + if (targetCount === 1) { + tickValues = [values[0]]; + } else if (targetCount === 2) { + tickValues = [values[0], values[values.length - 1]]; + } else if (targetCount > 2) { + count = targetCount - 2; + start = values[0]; + end = values[values.length - 1]; + interval = (end - start) / (count + 1); + // re-construct unique values + tickValues = [start]; + for (i = 0; i < count; i++) { + tickValue = +start + interval * (i + 1); + tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue); + } + tickValues.push(end); + } + } + if (!forTimeSeries) { + tickValues = tickValues.sort(function (a, b) { + return a - b; + }); + } + return tickValues; +}; +c3_axis_fn.generateTransitions = function generateTransitions(duration) { + var $$ = this.owner, + axes = $$.axes; + return { + axisX: duration ? axes.x.transition().duration(duration) : axes.x, + axisY: duration ? axes.y.transition().duration(duration) : axes.y, + axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2, + axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx + }; +}; +c3_axis_fn.redraw = function redraw(transitions, isHidden) { + var $$ = this.owner; + $$.axes.x.style("opacity", isHidden ? 0 : 1); + $$.axes.y.style("opacity", isHidden ? 0 : 1); + $$.axes.y2.style("opacity", isHidden ? 0 : 1); + $$.axes.subx.style("opacity", isHidden ? 0 : 1); + transitions.axisX.call($$.xAxis); + transitions.axisY.call($$.yAxis); + transitions.axisY2.call($$.y2Axis); + transitions.axisSubX.call($$.subXAxis); +}; + +var c3$1 = { version: "0.4.18" }; + +var c3_chart_fn; +var c3_chart_internal_fn; + +function Component(owner, componentKey, fn) { + this.owner = owner; + c3$1.chart.internal[componentKey] = fn; +} + +function Chart(config) { + var $$ = this.internal = new ChartInternal(this); + $$.loadConfig(config); + + $$.beforeInit(config); + $$.init(); + $$.afterInit(config); + + // bind "this" to nested API + (function bindThis(fn, target, argThis) { + Object.keys(fn).forEach(function (key) { + target[key] = fn[key].bind(argThis); + if (Object.keys(fn[key]).length > 0) { + bindThis(fn[key], target[key], argThis); + } + }); + })(c3_chart_fn, this, this); +} + +function ChartInternal(api) { + var $$ = this; + $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require("d3") : undefined; + $$.api = api; + $$.config = $$.getDefaultConfig(); + $$.data = {}; + $$.cache = {}; + $$.axes = {}; +} + +c3$1.generate = function (config) { + return new Chart(config); +}; + +c3$1.chart = { + fn: Chart.prototype, + internal: { + fn: ChartInternal.prototype + } +}; +c3_chart_fn = c3$1.chart.fn; +c3_chart_internal_fn = c3$1.chart.internal.fn; + +c3_chart_internal_fn.beforeInit = function () { + // can do something +}; +c3_chart_internal_fn.afterInit = function () { + // can do something +}; +c3_chart_internal_fn.init = function () { + var $$ = this, + config = $$.config; + + $$.initParams(); + + if (config.data_url) { + $$.convertUrlToData(config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData); + } else if (config.data_json) { + $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys)); + } else if (config.data_rows) { + $$.initWithData($$.convertRowsToData(config.data_rows)); + } else if (config.data_columns) { + $$.initWithData($$.convertColumnsToData(config.data_columns)); + } else { + throw Error('url or json or rows or columns is required.'); + } +}; + +c3_chart_internal_fn.initParams = function () { + var $$ = this, + d3 = $$.d3, + config = $$.config; + + // MEMO: clipId needs to be unique because it conflicts when multiple charts exist + $$.clipId = "c3-" + +new Date() + '-clip', $$.clipIdForXAxis = $$.clipId + '-xaxis', $$.clipIdForYAxis = $$.clipId + '-yaxis', $$.clipIdForGrid = $$.clipId + '-grid', $$.clipIdForSubchart = $$.clipId + '-subchart', $$.clipPath = $$.getClipPath($$.clipId), $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis), $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis); + $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid), $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart), $$.dragStart = null; + $$.dragging = false; + $$.flowing = false; + $$.cancelClick = false; + $$.mouseover = false; + $$.transiting = false; + + $$.color = $$.generateColor(); + $$.levelColor = $$.generateLevelColor(); + + $$.dataTimeFormat = config.data_xLocaltime ? d3.time.format : d3.time.format.utc; + $$.axisTimeFormat = config.axis_x_localtime ? d3.time.format : d3.time.format.utc; + $$.defaultAxisTimeFormat = $$.axisTimeFormat.multi([[".%L", function (d) { + return d.getMilliseconds(); + }], [":%S", function (d) { + return d.getSeconds(); + }], ["%I:%M", function (d) { + return d.getMinutes(); + }], ["%I %p", function (d) { + return d.getHours(); + }], ["%-m/%-d", function (d) { + return d.getDay() && d.getDate() !== 1; + }], ["%-m/%-d", function (d) { + return d.getDate() !== 1; + }], ["%-m/%-d", function (d) { + return d.getMonth(); + }], ["%Y/%-m/%-d", function () { + return true; + }]]); + + $$.hiddenTargetIds = []; + $$.hiddenLegendIds = []; + $$.focusedTargetIds = []; + $$.defocusedTargetIds = []; + + $$.xOrient = config.axis_rotated ? "left" : "bottom"; + $$.yOrient = config.axis_rotated ? config.axis_y_inner ? "top" : "bottom" : config.axis_y_inner ? "right" : "left"; + $$.y2Orient = config.axis_rotated ? config.axis_y2_inner ? "bottom" : "top" : config.axis_y2_inner ? "left" : "right"; + $$.subXOrient = config.axis_rotated ? "left" : "bottom"; + + $$.isLegendRight = config.legend_position === 'right'; + $$.isLegendInset = config.legend_position === 'inset'; + $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right'; + $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left'; + $$.legendStep = 0; + $$.legendItemWidth = 0; + $$.legendItemHeight = 0; + + $$.currentMaxTickWidths = { + x: 0, + y: 0, + y2: 0 + }; + + $$.rotated_padding_left = 30; + $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30; + $$.rotated_padding_top = 5; + + $$.withoutFadeIn = {}; + + $$.intervalForObserveInserted = undefined; + + $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js +}; + +c3_chart_internal_fn.initChartElements = function () { + if (this.initBar) { + this.initBar(); + } + if (this.initLine) { + this.initLine(); + } + if (this.initArc) { + this.initArc(); + } + if (this.initGauge) { + this.initGauge(); + } + if (this.initText) { + this.initText(); + } +}; + +c3_chart_internal_fn.initWithData = function (data) { + var $$ = this, + d3 = $$.d3, + config = $$.config; + var defs, + main, + binding = true; + + $$.axis = new Axis($$); + + if ($$.initPie) { + $$.initPie(); + } + if ($$.initBrush) { + $$.initBrush(); + } + if ($$.initZoom) { + $$.initZoom(); + } + + if (!config.bindto) { + $$.selectChart = d3.selectAll([]); + } else if (typeof config.bindto.node === 'function') { + $$.selectChart = config.bindto; + } else { + $$.selectChart = d3.select(config.bindto); + } + if ($$.selectChart.empty()) { + $$.selectChart = d3.select(document.createElement('div')).style('opacity', 0); + $$.observeInserted($$.selectChart); + binding = false; + } + $$.selectChart.html("").classed("c3", true); + + // Init data as targets + $$.data.xs = {}; + $$.data.targets = $$.convertDataToTargets(data); + + if (config.data_filter) { + $$.data.targets = $$.data.targets.filter(config.data_filter); + } + + // Set targets to hide if needed + if (config.data_hide) { + $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide); + } + if (config.legend_hide) { + $$.addHiddenLegendIds(config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide); + } + + // when gauge, hide legend // TODO: fix + if ($$.hasType('gauge')) { + config.legend_show = false; + } + + // Init sizes and scales + $$.updateSizes(); + $$.updateScales(); + + // Set domains for each scale + $$.x.domain(d3.extent($$.getXDomain($$.data.targets))); + $$.y.domain($$.getYDomain($$.data.targets, 'y')); + $$.y2.domain($$.getYDomain($$.data.targets, 'y2')); + $$.subX.domain($$.x.domain()); + $$.subY.domain($$.y.domain()); + $$.subY2.domain($$.y2.domain()); + + // Save original x domain for zoom update + $$.orgXDomain = $$.x.domain(); + + // Set initialized scales to brush and zoom + if ($$.brush) { + $$.brush.scale($$.subX); + } + if (config.zoom_enabled) { + $$.zoom.scale($$.x); + } + + /*-- Basic Elements --*/ + + // Define svgs + $$.svg = $$.selectChart.append("svg").style("overflow", "hidden").on('mouseenter', function () { + return config.onmouseover.call($$); + }).on('mouseleave', function () { + return config.onmouseout.call($$); + }); + + if ($$.config.svg_classname) { + $$.svg.attr('class', $$.config.svg_classname); + } + + // Define defs + defs = $$.svg.append("defs"); + $$.clipChart = $$.appendClip(defs, $$.clipId); + $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis); + $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis); + $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid); + $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart); + $$.updateSvgSize(); + + // Define regions + main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main')); + + if ($$.initSubchart) { + $$.initSubchart(); + } + if ($$.initTooltip) { + $$.initTooltip(); + } + if ($$.initLegend) { + $$.initLegend(); + } + if ($$.initTitle) { + $$.initTitle(); + } + + /*-- Main Region --*/ + + // text when empty + main.append("text").attr("class", CLASS.text + ' ' + CLASS.empty).attr("text-anchor", "middle" // horizontal centering of text at x position in all browsers. + ).attr("dominant-baseline", "middle"); // vertical centering of text at y position in all browsers, except IE. + + // Regions + $$.initRegion(); + + // Grids + $$.initGrid(); + + // Define g for chart area + main.append('g').attr("clip-path", $$.clipPath).attr('class', CLASS.chart); + + // Grid lines + if (config.grid_lines_front) { + $$.initGridLines(); + } + + // Cover whole with rects for events + $$.initEventRect(); + + // Define g for chart + $$.initChartElements(); + + // if zoom privileged, insert rect to forefront + // TODO: is this needed? + main.insert('rect', config.zoom_privileged ? null : 'g.' + CLASS.regions).attr('class', CLASS.zoomRect).attr('width', $$.width).attr('height', $$.height).style('opacity', 0).on("dblclick.zoom", null); + + // Set default extent if defined + if (config.axis_x_extent) { + $$.brush.extent($$.getDefaultExtent()); + } + + // Add Axis + $$.axis.init(); + + // Set targets + $$.updateTargets($$.data.targets); + + // Draw with targets + if (binding) { + $$.updateDimension(); + $$.config.oninit.call($$); + $$.redraw({ + withTransition: false, + withTransform: true, + withUpdateXDomain: true, + withUpdateOrgXDomain: true, + withTransitionForAxis: false + }); + } + + // Bind resize event + $$.bindResize(); + + // export element of the chart + $$.api.element = $$.selectChart.node(); +}; + +c3_chart_internal_fn.smoothLines = function (el, type) { + var $$ = this; + if (type === 'grid') { + el.each(function () { + var g = $$.d3.select(this), + x1 = g.attr('x1'), + x2 = g.attr('x2'), + y1 = g.attr('y1'), + y2 = g.attr('y2'); + g.attr({ + 'x1': Math.ceil(x1), + 'x2': Math.ceil(x2), + 'y1': Math.ceil(y1), + 'y2': Math.ceil(y2) + }); + }); + } +}; + +c3_chart_internal_fn.updateSizes = function () { + var $$ = this, + config = $$.config; + var legendHeight = $$.legend ? $$.getLegendHeight() : 0, + legendWidth = $$.legend ? $$.getLegendWidth() : 0, + legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight, + hasArc = $$.hasArcType(), + xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'), + subchartHeight = config.subchart_show && !hasArc ? config.subchart_size_height + xAxisHeight : 0; + + $$.currentWidth = $$.getCurrentWidth(); + $$.currentHeight = $$.getCurrentHeight(); + + // for main + $$.margin = config.axis_rotated ? { + top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(), + right: hasArc ? 0 : $$.getCurrentPaddingRight(), + bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(), + left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft()) + } : { + top: 4 + $$.getCurrentPaddingTop(), // for top tick text + right: hasArc ? 0 : $$.getCurrentPaddingRight(), + bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(), + left: hasArc ? 0 : $$.getCurrentPaddingLeft() + }; + + // for subchart + $$.margin2 = config.axis_rotated ? { + top: $$.margin.top, + right: NaN, + bottom: 20 + legendHeightForBottom, + left: $$.rotated_padding_left + } : { + top: $$.currentHeight - subchartHeight - legendHeightForBottom, + right: NaN, + bottom: xAxisHeight + legendHeightForBottom, + left: $$.margin.left + }; + + // for legend + $$.margin3 = { + top: 0, + right: NaN, + bottom: 0, + left: 0 + }; + if ($$.updateSizeForLegend) { + $$.updateSizeForLegend(legendHeight, legendWidth); + } + + $$.width = $$.currentWidth - $$.margin.left - $$.margin.right; + $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom; + if ($$.width < 0) { + $$.width = 0; + } + if ($$.height < 0) { + $$.height = 0; + } + + $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width; + $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom; + if ($$.width2 < 0) { + $$.width2 = 0; + } + if ($$.height2 < 0) { + $$.height2 = 0; + } + + // for arc + $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0); + $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10); + if ($$.hasType('gauge') && !config.gauge_fullCircle) { + $$.arcHeight += $$.height - $$.getGaugeLabelHeight(); + } + if ($$.updateRadius) { + $$.updateRadius(); + } + + if ($$.isLegendRight && hasArc) { + $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1; + } +}; + +c3_chart_internal_fn.updateTargets = function (targets) { + var $$ = this; + + /*-- Main --*/ + + //-- Text --// + $$.updateTargetsForText(targets); + + //-- Bar --// + $$.updateTargetsForBar(targets); + + //-- Line --// + $$.updateTargetsForLine(targets); + + //-- Arc --// + if ($$.hasArcType() && $$.updateTargetsForArc) { + $$.updateTargetsForArc(targets); + } + + /*-- Sub --*/ + + if ($$.updateTargetsForSubchart) { + $$.updateTargetsForSubchart(targets); + } + + // Fade-in each chart + $$.showTargets(); +}; +c3_chart_internal_fn.showTargets = function () { + var $$ = this; + $$.svg.selectAll('.' + CLASS.target).filter(function (d) { + return $$.isTargetToShow(d.id); + }).transition().duration($$.config.transition_duration).style("opacity", 1); +}; + +c3_chart_internal_fn.redraw = function (options, transitions) { + var $$ = this, + main = $$.main, + d3 = $$.d3, + config = $$.config; + var areaIndices = $$.getShapeIndices($$.isAreaType), + barIndices = $$.getShapeIndices($$.isBarType), + lineIndices = $$.getShapeIndices($$.isLineType); + var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect, withDimension, withUpdateXAxis; + var hideAxis = $$.hasArcType(); + var drawArea, drawBar, drawLine, xForText, yForText; + var duration, durationForExit, durationForAxis; + var waitForDraw, flow; + var targetsToShow = $$.filterTargetsToShow($$.data.targets), + tickValues, + i, + intervalForCulling, + xDomainForZoom; + var xv = $$.xv.bind($$), + cx, + cy; + + options = options || {}; + withY = getOption(options, "withY", true); + withSubchart = getOption(options, "withSubchart", true); + withTransition = getOption(options, "withTransition", true); + withTransform = getOption(options, "withTransform", false); + withUpdateXDomain = getOption(options, "withUpdateXDomain", false); + withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", false); + withTrimXDomain = getOption(options, "withTrimXDomain", true); + withUpdateXAxis = getOption(options, "withUpdateXAxis", withUpdateXDomain); + withLegend = getOption(options, "withLegend", false); + withEventRect = getOption(options, "withEventRect", true); + withDimension = getOption(options, "withDimension", true); + withTransitionForExit = getOption(options, "withTransitionForExit", withTransition); + withTransitionForAxis = getOption(options, "withTransitionForAxis", withTransition); + + duration = withTransition ? config.transition_duration : 0; + durationForExit = withTransitionForExit ? duration : 0; + durationForAxis = withTransitionForAxis ? duration : 0; + + transitions = transitions || $$.axis.generateTransitions(durationForAxis); + + // update legend and transform each g + if (withLegend && config.legend_show) { + $$.updateLegend($$.mapToIds($$.data.targets), options, transitions); + } else if (withDimension) { + // need to update dimension (e.g. axis.y.tick.values) because y tick values should change + // no need to update axis in it because they will be updated in redraw() + $$.updateDimension(true); + } + + // MEMO: needed for grids calculation + if ($$.isCategorized() && targetsToShow.length === 0) { + $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]); + } + + if (targetsToShow.length) { + $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain); + if (!config.axis_x_tick_values) { + tickValues = $$.axis.updateXAxisTickValues(targetsToShow); + } + } else { + $$.xAxis.tickValues([]); + $$.subXAxis.tickValues([]); + } + + if (config.zoom_rescale && !options.flow) { + xDomainForZoom = $$.x.orgDomain(); + } + + $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom)); + $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom)); + + if (!config.axis_y_tick_values && config.axis_y_tick_count) { + $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count)); + } + if (!config.axis_y2_tick_values && config.axis_y2_tick_count) { + $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count)); + } + + // axes + $$.axis.redraw(transitions, hideAxis); + + // Update axis label + $$.axis.updateLabels(withTransition); + + // show/hide if manual culling needed + if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) { + if (config.axis_x_tick_culling && tickValues) { + for (i = 1; i < tickValues.length; i++) { + if (tickValues.length / i < config.axis_x_tick_culling_max) { + intervalForCulling = i; + break; + } + } + $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) { + var index = tickValues.indexOf(e); + if (index >= 0) { + d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block'); + } + }); + } else { + $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').style('display', 'block'); + } + } + + // setup drawer - MEMO: these must be called after axis updated + drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined; + drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined; + drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined; + xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true); + yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false); + + // Update sub domain + if (withY) { + $$.subY.domain($$.getYDomain(targetsToShow, 'y')); + $$.subY2.domain($$.getYDomain(targetsToShow, 'y2')); + } + + // xgrid focus + $$.updateXgridFocus(); + + // Data empty label positioning and text. + main.select("text." + CLASS.text + '.' + CLASS.empty).attr("x", $$.width / 2).attr("y", $$.height / 2).text(config.data_empty_label_text).transition().style('opacity', targetsToShow.length ? 0 : 1); + + // grid + $$.updateGrid(duration); + + // rect for regions + $$.updateRegion(duration); + + // bars + $$.updateBar(durationForExit); + + // lines, areas and cricles + $$.updateLine(durationForExit); + $$.updateArea(durationForExit); + $$.updateCircle(); + + // text + if ($$.hasDataLabel()) { + $$.updateText(durationForExit); + } + + // title + if ($$.redrawTitle) { + $$.redrawTitle(); + } + + // arc + if ($$.redrawArc) { + $$.redrawArc(duration, durationForExit, withTransform); + } + + // subchart + if ($$.redrawSubchart) { + $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices); + } + + // circles for select + main.selectAll('.' + CLASS.selectedCircles).filter($$.isBarType.bind($$)).selectAll('circle').remove(); + + // event rects will redrawn when flow called + if (config.interaction_enabled && !options.flow && withEventRect) { + $$.redrawEventRect(); + if ($$.updateZoom) { + $$.updateZoom(); + } + } + + // update circleY based on updated parameters + $$.updateCircleY(); + + // generate circle x/y functions depending on updated params + cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$); + cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$); + + if (options.flow) { + flow = $$.generateFlow({ + targets: targetsToShow, + flow: options.flow, + duration: options.flow.duration, + drawBar: drawBar, + drawLine: drawLine, + drawArea: drawArea, + cx: cx, + cy: cy, + xv: xv, + xForText: xForText, + yForText: yForText + }); + } + + if ((duration || flow) && $$.isTabVisible()) { + // Only use transition if tab visible. See #938. + // transition should be derived from one transition + d3.transition().duration(duration).each(function () { + var transitionsToWait = []; + + // redraw and gather transitions + [$$.redrawBar(drawBar, true), $$.redrawLine(drawLine, true), $$.redrawArea(drawArea, true), $$.redrawCircle(cx, cy, true), $$.redrawText(xForText, yForText, options.flow, true), $$.redrawRegion(true), $$.redrawGrid(true)].forEach(function (transitions) { + transitions.forEach(function (transition) { + transitionsToWait.push(transition); + }); + }); + + // Wait for end of transitions to call flow and onrendered callback + waitForDraw = $$.generateWait(); + transitionsToWait.forEach(function (t) { + waitForDraw.add(t); + }); + }).call(waitForDraw, function () { + if (flow) { + flow(); + } + if (config.onrendered) { + config.onrendered.call($$); + } + }); + } else { + $$.redrawBar(drawBar); + $$.redrawLine(drawLine); + $$.redrawArea(drawArea); + $$.redrawCircle(cx, cy); + $$.redrawText(xForText, yForText, options.flow); + $$.redrawRegion(); + $$.redrawGrid(); + if (config.onrendered) { + config.onrendered.call($$); + } + } + + // update fadein condition + $$.mapToIds($$.data.targets).forEach(function (id) { + $$.withoutFadeIn[id] = true; + }); +}; + +c3_chart_internal_fn.updateAndRedraw = function (options) { + var $$ = this, + config = $$.config, + transitions; + options = options || {}; + // same with redraw + options.withTransition = getOption(options, "withTransition", true); + options.withTransform = getOption(options, "withTransform", false); + options.withLegend = getOption(options, "withLegend", false); + // NOT same with redraw + options.withUpdateXDomain = true; + options.withUpdateOrgXDomain = true; + options.withTransitionForExit = false; + options.withTransitionForTransform = getOption(options, "withTransitionForTransform", options.withTransition); + // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called) + $$.updateSizes(); + // MEMO: called in updateLegend in redraw if withLegend + if (!(options.withLegend && config.legend_show)) { + transitions = $$.axis.generateTransitions(options.withTransitionForAxis ? config.transition_duration : 0); + // Update scales + $$.updateScales(); + $$.updateSvgSize(); + // Update g positions + $$.transformAll(options.withTransitionForTransform, transitions); + } + // Draw with new sizes & scales + $$.redraw(options, transitions); +}; +c3_chart_internal_fn.redrawWithoutRescale = function () { + this.redraw({ + withY: false, + withSubchart: false, + withEventRect: false, + withTransitionForAxis: false + }); +}; + +c3_chart_internal_fn.isTimeSeries = function () { + return this.config.axis_x_type === 'timeseries'; +}; +c3_chart_internal_fn.isCategorized = function () { + return this.config.axis_x_type.indexOf('categor') >= 0; +}; +c3_chart_internal_fn.isCustomX = function () { + var $$ = this, + config = $$.config; + return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs)); +}; + +c3_chart_internal_fn.isTimeSeriesY = function () { + return this.config.axis_y_type === 'timeseries'; +}; + +c3_chart_internal_fn.getTranslate = function (target) { + var $$ = this, + config = $$.config, + x, + y; + if (target === 'main') { + x = asHalfPixel($$.margin.left); + y = asHalfPixel($$.margin.top); + } else if (target === 'context') { + x = asHalfPixel($$.margin2.left); + y = asHalfPixel($$.margin2.top); + } else if (target === 'legend') { + x = $$.margin3.left; + y = $$.margin3.top; + } else if (target === 'x') { + x = 0; + y = config.axis_rotated ? 0 : $$.height; + } else if (target === 'y') { + x = 0; + y = config.axis_rotated ? $$.height : 0; + } else if (target === 'y2') { + x = config.axis_rotated ? 0 : $$.width; + y = config.axis_rotated ? 1 : 0; + } else if (target === 'subx') { + x = 0; + y = config.axis_rotated ? 0 : $$.height2; + } else if (target === 'arc') { + x = $$.arcWidth / 2; + y = $$.arcHeight / 2; + } + return "translate(" + x + "," + y + ")"; +}; +c3_chart_internal_fn.initialOpacity = function (d) { + return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0; +}; +c3_chart_internal_fn.initialOpacityForCircle = function (d) { + return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0; +}; +c3_chart_internal_fn.opacityForCircle = function (d) { + var isPointShouldBeShown = isFunction(this.config.point_show) ? this.config.point_show(d) : this.config.point_show; + var opacity = isPointShouldBeShown ? 1 : 0; + return isValue(d.value) ? this.isScatterType(d) ? 0.5 : opacity : 0; +}; +c3_chart_internal_fn.opacityForText = function () { + return this.hasDataLabel() ? 1 : 0; +}; +c3_chart_internal_fn.xx = function (d) { + return d ? this.x(d.x) : null; +}; +c3_chart_internal_fn.xv = function (d) { + var $$ = this, + value = d.value; + if ($$.isTimeSeries()) { + value = $$.parseDate(d.value); + } else if ($$.isCategorized() && typeof d.value === 'string') { + value = $$.config.axis_x_categories.indexOf(d.value); + } + return Math.ceil($$.x(value)); +}; +c3_chart_internal_fn.yv = function (d) { + var $$ = this, + yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y; + return Math.ceil(yScale(d.value)); +}; +c3_chart_internal_fn.subxx = function (d) { + return d ? this.subX(d.x) : null; +}; + +c3_chart_internal_fn.transformMain = function (withTransition, transitions) { + var $$ = this, + xAxis, + yAxis, + y2Axis; + if (transitions && transitions.axisX) { + xAxis = transitions.axisX; + } else { + xAxis = $$.main.select('.' + CLASS.axisX); + if (withTransition) { + xAxis = xAxis.transition(); + } + } + if (transitions && transitions.axisY) { + yAxis = transitions.axisY; + } else { + yAxis = $$.main.select('.' + CLASS.axisY); + if (withTransition) { + yAxis = yAxis.transition(); + } + } + if (transitions && transitions.axisY2) { + y2Axis = transitions.axisY2; + } else { + y2Axis = $$.main.select('.' + CLASS.axisY2); + if (withTransition) { + y2Axis = y2Axis.transition(); + } + } + (withTransition ? $$.main.transition() : $$.main).attr("transform", $$.getTranslate('main')); + xAxis.attr("transform", $$.getTranslate('x')); + yAxis.attr("transform", $$.getTranslate('y')); + y2Axis.attr("transform", $$.getTranslate('y2')); + $$.main.select('.' + CLASS.chartArcs).attr("transform", $$.getTranslate('arc')); +}; +c3_chart_internal_fn.transformAll = function (withTransition, transitions) { + var $$ = this; + $$.transformMain(withTransition, transitions); + if ($$.config.subchart_show) { + $$.transformContext(withTransition, transitions); + } + if ($$.legend) { + $$.transformLegend(withTransition); + } +}; + +c3_chart_internal_fn.updateSvgSize = function () { + var $$ = this, + brush = $$.svg.select(".c3-brush .background"); + $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight); + $$.svg.selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]).select('rect').attr('width', $$.width).attr('height', $$.height); + $$.svg.select('#' + $$.clipIdForXAxis).select('rect').attr('x', $$.getXAxisClipX.bind($$)).attr('y', $$.getXAxisClipY.bind($$)).attr('width', $$.getXAxisClipWidth.bind($$)).attr('height', $$.getXAxisClipHeight.bind($$)); + $$.svg.select('#' + $$.clipIdForYAxis).select('rect').attr('x', $$.getYAxisClipX.bind($$)).attr('y', $$.getYAxisClipY.bind($$)).attr('width', $$.getYAxisClipWidth.bind($$)).attr('height', $$.getYAxisClipHeight.bind($$)); + $$.svg.select('#' + $$.clipIdForSubchart).select('rect').attr('width', $$.width).attr('height', brush.size() ? brush.attr('height') : 0); + $$.svg.select('.' + CLASS.zoomRect).attr('width', $$.width).attr('height', $$.height); + // MEMO: parent div's height will be bigger than svg when + $$.selectChart.style('max-height', $$.currentHeight + "px"); +}; + +c3_chart_internal_fn.updateDimension = function (withoutAxis) { + var $$ = this; + if (!withoutAxis) { + if ($$.config.axis_rotated) { + $$.axes.x.call($$.xAxis); + $$.axes.subx.call($$.subXAxis); + } else { + $$.axes.y.call($$.yAxis); + $$.axes.y2.call($$.y2Axis); + } + } + $$.updateSizes(); + $$.updateScales(); + $$.updateSvgSize(); + $$.transformAll(false); +}; + +c3_chart_internal_fn.observeInserted = function (selection) { + var $$ = this, + observer; + if (typeof MutationObserver === 'undefined') { + window.console.error("MutationObserver not defined."); + return; + } + observer = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + if (mutation.type === 'childList' && mutation.previousSibling) { + observer.disconnect(); + // need to wait for completion of load because size calculation requires the actual sizes determined after that completion + $$.intervalForObserveInserted = window.setInterval(function () { + // parentNode will NOT be null when completed + if (selection.node().parentNode) { + window.clearInterval($$.intervalForObserveInserted); + $$.updateDimension(); + if ($$.brush) { + $$.brush.update(); + } + $$.config.oninit.call($$); + $$.redraw({ + withTransform: true, + withUpdateXDomain: true, + withUpdateOrgXDomain: true, + withTransition: false, + withTransitionForTransform: false, + withLegend: true + }); + selection.transition().style('opacity', 1); + } + }, 10); + } + }); + }); + observer.observe(selection.node(), { attributes: true, childList: true, characterData: true }); +}; + +c3_chart_internal_fn.bindResize = function () { + var $$ = this, + config = $$.config; + + $$.resizeFunction = $$.generateResize(); + + $$.resizeFunction.add(function () { + config.onresize.call($$); + }); + if (config.resize_auto) { + $$.resizeFunction.add(function () { + if ($$.resizeTimeout !== undefined) { + window.clearTimeout($$.resizeTimeout); + } + $$.resizeTimeout = window.setTimeout(function () { + delete $$.resizeTimeout; + $$.api.flush(); + }, 100); + }); + } + $$.resizeFunction.add(function () { + config.onresized.call($$); + }); + + if (window.attachEvent) { + window.attachEvent('onresize', $$.resizeFunction); + } else if (window.addEventListener) { + window.addEventListener('resize', $$.resizeFunction, false); + } else { + // fallback to this, if this is a very old browser + var wrapper = window.onresize; + if (!wrapper) { + // create a wrapper that will call all charts + wrapper = $$.generateResize(); + } else if (!wrapper.add || !wrapper.remove) { + // there is already a handler registered, make sure we call it too + wrapper = $$.generateResize(); + wrapper.add(window.onresize); + } + // add this graph to the wrapper, we will be removed if the user calls destroy + wrapper.add($$.resizeFunction); + window.onresize = wrapper; + } +}; + +c3_chart_internal_fn.generateResize = function () { + var resizeFunctions = []; + function callResizeFunctions() { + resizeFunctions.forEach(function (f) { + f(); + }); + } + callResizeFunctions.add = function (f) { + resizeFunctions.push(f); + }; + callResizeFunctions.remove = function (f) { + for (var i = 0; i < resizeFunctions.length; i++) { + if (resizeFunctions[i] === f) { + resizeFunctions.splice(i, 1); + break; + } + } + }; + return callResizeFunctions; +}; + +c3_chart_internal_fn.endall = function (transition, callback) { + var n = 0; + transition.each(function () { + ++n; + }).each("end", function () { + if (! --n) { + callback.apply(this, arguments); + } + }); +}; +c3_chart_internal_fn.generateWait = function () { + var transitionsToWait = [], + f = function f(transition, callback) { + var timer = setInterval(function () { + var done = 0; + transitionsToWait.forEach(function (t) { + if (t.empty()) { + done += 1; + return; + } + try { + t.transition(); + } catch (e) { + done += 1; + } + }); + if (done === transitionsToWait.length) { + clearInterval(timer); + if (callback) { + callback(); + } + } + }, 10); + }; + f.add = function (transition) { + transitionsToWait.push(transition); + }; + return f; +}; + +c3_chart_internal_fn.parseDate = function (date) { + var $$ = this, + parsedDate; + if (date instanceof Date) { + parsedDate = date; + } else if (typeof date === 'string') { + parsedDate = $$.dataTimeFormat($$.config.data_xFormat).parse(date); + } else if ((typeof date === 'undefined' ? 'undefined' : _typeof(date)) === 'object') { + parsedDate = new Date(+date); + } else if (typeof date === 'number' && !isNaN(date)) { + parsedDate = new Date(+date); + } + if (!parsedDate || isNaN(+parsedDate)) { + window.console.error("Failed to parse x '" + date + "' to Date object"); + } + return parsedDate; +}; + +c3_chart_internal_fn.isTabVisible = function () { + var hidden; + if (typeof document.hidden !== "undefined") { + // Opera 12.10 and Firefox 18 and later support + hidden = "hidden"; + } else if (typeof document.mozHidden !== "undefined") { + hidden = "mozHidden"; + } else if (typeof document.msHidden !== "undefined") { + hidden = "msHidden"; + } else if (typeof document.webkitHidden !== "undefined") { + hidden = "webkitHidden"; + } + + return document[hidden] ? false : true; +}; + +c3_chart_internal_fn.isValue = isValue; +c3_chart_internal_fn.isFunction = isFunction; +c3_chart_internal_fn.isString = isString; +c3_chart_internal_fn.isUndefined = isUndefined; +c3_chart_internal_fn.isDefined = isDefined; +c3_chart_internal_fn.ceil10 = ceil10; +c3_chart_internal_fn.asHalfPixel = asHalfPixel; +c3_chart_internal_fn.diffDomain = diffDomain; +c3_chart_internal_fn.isEmpty = isEmpty; +c3_chart_internal_fn.notEmpty = notEmpty; +c3_chart_internal_fn.notEmpty = notEmpty; +c3_chart_internal_fn.getOption = getOption; +c3_chart_internal_fn.hasValue = hasValue; +c3_chart_internal_fn.sanitise = sanitise; +c3_chart_internal_fn.getPathBox = getPathBox; +c3_chart_internal_fn.CLASS = CLASS; + +/* jshint ignore:start */ + +// PhantomJS doesn't have support for Function.prototype.bind, which has caused confusion. Use +// this polyfill to avoid the confusion. +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill + +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function fNOP() {}, + fBound = function fBound() { + return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; +} + +// SVGPathSeg API polyfill +// https://github.com/progers/pathseg +// +// This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from +// SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec +// changes which were implemented in Firefox 43 and Chrome 46. + +(function () { + "use strict"; + + if (!("SVGPathSeg" in window)) { + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg + window.SVGPathSeg = function (type, typeAsLetter, owningPathSegList) { + this.pathSegType = type; + this.pathSegTypeAsLetter = typeAsLetter; + this._owningPathSegList = owningPathSegList; + }; + + window.SVGPathSeg.prototype.classname = "SVGPathSeg"; + + window.SVGPathSeg.PATHSEG_UNKNOWN = 0; + window.SVGPathSeg.PATHSEG_CLOSEPATH = 1; + window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2; + window.SVGPathSeg.PATHSEG_MOVETO_REL = 3; + window.SVGPathSeg.PATHSEG_LINETO_ABS = 4; + window.SVGPathSeg.PATHSEG_LINETO_REL = 5; + window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; + window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; + window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; + window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; + window.SVGPathSeg.PATHSEG_ARC_ABS = 10; + window.SVGPathSeg.PATHSEG_ARC_REL = 11; + window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; + window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; + window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; + window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; + window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; + window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; + window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; + window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; + + // Notify owning PathSegList on any changes so they can be synchronized back to the path element. + window.SVGPathSeg.prototype._segmentChanged = function () { + if (this._owningPathSegList) this._owningPathSegList.segmentChanged(this); + }; + + window.SVGPathSegClosePath = function (owningPathSegList) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList); + }; + window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegClosePath.prototype.toString = function () { + return "[object SVGPathSegClosePath]"; + }; + window.SVGPathSegClosePath.prototype._asPathString = function () { + return this.pathSegTypeAsLetter; + }; + window.SVGPathSegClosePath.prototype.clone = function () { + return new window.SVGPathSegClosePath(undefined); + }; + + window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegMovetoAbs.prototype.toString = function () { + return "[object SVGPathSegMovetoAbs]"; + }; + window.SVGPathSegMovetoAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; + }; + window.SVGPathSegMovetoAbs.prototype.clone = function () { + return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y); + }; + Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegMovetoRel.prototype.toString = function () { + return "[object SVGPathSegMovetoRel]"; + }; + window.SVGPathSegMovetoRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; + }; + window.SVGPathSegMovetoRel.prototype.clone = function () { + return new window.SVGPathSegMovetoRel(undefined, this._x, this._y); + }; + Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoAbs.prototype.toString = function () { + return "[object SVGPathSegLinetoAbs]"; + }; + window.SVGPathSegLinetoAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; + }; + window.SVGPathSegLinetoAbs.prototype.clone = function () { + return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y); + }; + Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoRel.prototype.toString = function () { + return "[object SVGPathSegLinetoRel]"; + }; + window.SVGPathSegLinetoRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; + }; + window.SVGPathSegLinetoRel.prototype.clone = function () { + return new window.SVGPathSegLinetoRel(undefined, this._x, this._y); + }; + Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + }; + window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () { + return "[object SVGPathSegCurvetoCubicAbs]"; + }; + window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; + }; + window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () { + return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); + }; + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x1", { get: function get() { + return this._x1; + }, set: function set(x1) { + this._x1 = x1;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y1", { get: function get() { + return this._y1; + }, set: function set(y1) { + this._y1 = y1;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x2", { get: function get() { + return this._x2; + }, set: function set(x2) { + this._x2 = x2;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y2", { get: function get() { + return this._y2; + }, set: function set(y2) { + this._y2 = y2;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + this._x2 = x2; + this._y2 = y2; + }; + window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoCubicRel.prototype.toString = function () { + return "[object SVGPathSegCurvetoCubicRel]"; + }; + window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; + }; + window.SVGPathSegCurvetoCubicRel.prototype.clone = function () { + return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); + }; + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x1", { get: function get() { + return this._x1; + }, set: function set(x1) { + this._x1 = x1;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y1", { get: function get() { + return this._y1; + }, set: function set(y1) { + this._y1 = y1;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x2", { get: function get() { + return this._x2; + }, set: function set(x2) { + this._x2 = x2;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y2", { get: function get() { + return this._y2; + }, set: function set(y2) { + this._y2 = y2;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + }; + window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () { + return "[object SVGPathSegCurvetoQuadraticAbs]"; + }; + window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; + }; + window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () { + return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); + }; + Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x1", { get: function get() { + return this._x1; + }, set: function set(x1) { + this._x1 = x1;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y1", { get: function get() { + return this._y1; + }, set: function set(y1) { + this._y1 = y1;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList); + this._x = x; + this._y = y; + this._x1 = x1; + this._y1 = y1; + }; + window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () { + return "[object SVGPathSegCurvetoQuadraticRel]"; + }; + window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; + }; + window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () { + return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); + }; + Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x1", { get: function get() { + return this._x1; + }, set: function set(x1) { + this._x1 = x1;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y1", { get: function get() { + return this._y1; + }, set: function set(y1) { + this._y1 = y1;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList); + this._x = x; + this._y = y; + this._r1 = r1; + this._r2 = r2; + this._angle = angle; + this._largeArcFlag = largeArcFlag; + this._sweepFlag = sweepFlag; + }; + window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegArcAbs.prototype.toString = function () { + return "[object SVGPathSegArcAbs]"; + }; + window.SVGPathSegArcAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; + }; + window.SVGPathSegArcAbs.prototype.clone = function () { + return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); + }; + Object.defineProperty(window.SVGPathSegArcAbs.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r1", { get: function get() { + return this._r1; + }, set: function set(r1) { + this._r1 = r1;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r2", { get: function get() { + return this._r2; + }, set: function set(r2) { + this._r2 = r2;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, "angle", { get: function get() { + return this._angle; + }, set: function set(angle) { + this._angle = angle;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, "largeArcFlag", { get: function get() { + return this._largeArcFlag; + }, set: function set(largeArcFlag) { + this._largeArcFlag = largeArcFlag;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcAbs.prototype, "sweepFlag", { get: function get() { + return this._sweepFlag; + }, set: function set(sweepFlag) { + this._sweepFlag = sweepFlag;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList); + this._x = x; + this._y = y; + this._r1 = r1; + this._r2 = r2; + this._angle = angle; + this._largeArcFlag = largeArcFlag; + this._sweepFlag = sweepFlag; + }; + window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegArcRel.prototype.toString = function () { + return "[object SVGPathSegArcRel]"; + }; + window.SVGPathSegArcRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; + }; + window.SVGPathSegArcRel.prototype.clone = function () { + return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); + }; + Object.defineProperty(window.SVGPathSegArcRel.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, "r1", { get: function get() { + return this._r1; + }, set: function set(r1) { + this._r1 = r1;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, "r2", { get: function get() { + return this._r2; + }, set: function set(r2) { + this._r2 = r2;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, "angle", { get: function get() { + return this._angle; + }, set: function set(angle) { + this._angle = angle;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, "largeArcFlag", { get: function get() { + return this._largeArcFlag; + }, set: function set(largeArcFlag) { + this._largeArcFlag = largeArcFlag;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegArcRel.prototype, "sweepFlag", { get: function get() { + return this._sweepFlag; + }, set: function set(sweepFlag) { + this._sweepFlag = sweepFlag;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList); + this._x = x; + }; + window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () { + return "[object SVGPathSegLinetoHorizontalAbs]"; + }; + window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x; + }; + window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () { + return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x); + }; + Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList); + this._x = x; + }; + window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () { + return "[object SVGPathSegLinetoHorizontalRel]"; + }; + window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x; + }; + window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () { + return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x); + }; + Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList); + this._y = y; + }; + window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () { + return "[object SVGPathSegLinetoVerticalAbs]"; + }; + window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._y; + }; + window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () { + return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y); + }; + Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList); + this._y = y; + }; + window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegLinetoVerticalRel.prototype.toString = function () { + return "[object SVGPathSegLinetoVerticalRel]"; + }; + window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._y; + }; + window.SVGPathSegLinetoVerticalRel.prototype.clone = function () { + return new window.SVGPathSegLinetoVerticalRel(undefined, this._y); + }; + Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList); + this._x = x; + this._y = y; + this._x2 = x2; + this._y2 = y2; + }; + window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () { + return "[object SVGPathSegCurvetoCubicSmoothAbs]"; + }; + window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; + }; + window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () { + return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); + }; + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", { get: function get() { + return this._x2; + }, set: function set(x2) { + this._x2 = x2;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", { get: function get() { + return this._y2; + }, set: function set(y2) { + this._y2 = y2;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList); + this._x = x; + this._y = y; + this._x2 = x2; + this._y2 = y2; + }; + window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () { + return "[object SVGPathSegCurvetoCubicSmoothRel]"; + }; + window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; + }; + window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () { + return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); + }; + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", { get: function get() { + return this._x2; + }, set: function set(x2) { + this._x2 = x2;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", { get: function get() { + return this._y2; + }, set: function set(y2) { + this._y2 = y2;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () { + return "[object SVGPathSegCurvetoQuadraticSmoothAbs]"; + }; + window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; + }; + window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () { + return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); + }; + Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + + window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) { + window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList); + this._x = x; + this._y = y; + }; + window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); + window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () { + return "[object SVGPathSegCurvetoQuadraticSmoothRel]"; + }; + window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () { + return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; + }; + window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () { + return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); + }; + Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", { get: function get() { + return this._x; + }, set: function set(x) { + this._x = x;this._segmentChanged(); + }, enumerable: true }); + Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", { get: function get() { + return this._y; + }, set: function set(y) { + this._y = y;this._segmentChanged(); + }, enumerable: true }); + + // Add createSVGPathSeg* functions to window.SVGPathElement. + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement. + window.SVGPathElement.prototype.createSVGPathSegClosePath = function () { + return new window.SVGPathSegClosePath(undefined); + }; + window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) { + return new window.SVGPathSegMovetoAbs(undefined, x, y); + }; + window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) { + return new window.SVGPathSegMovetoRel(undefined, x, y); + }; + window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) { + return new window.SVGPathSegLinetoAbs(undefined, x, y); + }; + window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) { + return new window.SVGPathSegLinetoRel(undefined, x, y); + }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) { + return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); + }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) { + return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); + }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) { + return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); + }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) { + return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); + }; + window.SVGPathElement.prototype.createSVGPathSegArcAbs = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { + return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); + }; + window.SVGPathElement.prototype.createSVGPathSegArcRel = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { + return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); + }; + window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) { + return new window.SVGPathSegLinetoHorizontalAbs(undefined, x); + }; + window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) { + return new window.SVGPathSegLinetoHorizontalRel(undefined, x); + }; + window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) { + return new window.SVGPathSegLinetoVerticalAbs(undefined, y); + }; + window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) { + return new window.SVGPathSegLinetoVerticalRel(undefined, y); + }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) { + return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); + }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) { + return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); + }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) { + return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); + }; + window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) { + return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); + }; + + if (!("getPathSegAtLength" in window.SVGPathElement.prototype)) { + // Add getPathSegAtLength to SVGPathElement. + // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength + // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm. + window.SVGPathElement.prototype.getPathSegAtLength = function (distance) { + if (distance === undefined || !isFinite(distance)) throw "Invalid arguments."; + + var measurementElement = document.createElementNS("http://www.w3.org/2000/svg", "path"); + measurementElement.setAttribute("d", this.getAttribute("d")); + var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1; + + // If the path is empty, return 0. + if (lastPathSegment <= 0) return 0; + + do { + measurementElement.pathSegList.removeItem(lastPathSegment); + if (distance > measurementElement.getTotalLength()) break; + lastPathSegment--; + } while (lastPathSegment > 0); + return lastPathSegment; + }; + } + } + + if (!("SVGPathSegList" in window)) { + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList + window.SVGPathSegList = function (pathElement) { + this._pathElement = pathElement; + this._list = this._parsePath(this._pathElement.getAttribute("d")); + + // Use a MutationObserver to catch changes to the path's "d" attribute. + this._mutationObserverConfig = { "attributes": true, "attributeFilter": ["d"] }; + this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this)); + this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); + }; + + window.SVGPathSegList.prototype.classname = "SVGPathSegList"; + + Object.defineProperty(window.SVGPathSegList.prototype, "numberOfItems", { + get: function get() { + this._checkPathSynchronizedToList(); + return this._list.length; + }, + enumerable: true + }); + + // Add the pathSegList accessors to window.SVGPathElement. + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData + Object.defineProperty(window.SVGPathElement.prototype, "pathSegList", { + get: function get() { + if (!this._pathSegList) this._pathSegList = new window.SVGPathSegList(this); + return this._pathSegList; + }, + enumerable: true + }); + // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList. + Object.defineProperty(window.SVGPathElement.prototype, "normalizedPathSegList", { get: function get() { + return this.pathSegList; + }, enumerable: true }); + Object.defineProperty(window.SVGPathElement.prototype, "animatedPathSegList", { get: function get() { + return this.pathSegList; + }, enumerable: true }); + Object.defineProperty(window.SVGPathElement.prototype, "animatedNormalizedPathSegList", { get: function get() { + return this.pathSegList; + }, enumerable: true }); + + // Process any pending mutations to the path element and update the list as needed. + // This should be the first call of all public functions and is needed because + // MutationObservers are not synchronous so we can have pending asynchronous mutations. + window.SVGPathSegList.prototype._checkPathSynchronizedToList = function () { + this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords()); + }; + + window.SVGPathSegList.prototype._updateListFromPathMutations = function (mutationRecords) { + if (!this._pathElement) return; + var hasPathMutations = false; + mutationRecords.forEach(function (record) { + if (record.attributeName == "d") hasPathMutations = true; + }); + if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute("d")); + }; + + // Serialize the list and update the path's 'd' attribute. + window.SVGPathSegList.prototype._writeListToPath = function () { + this._pathElementMutationObserver.disconnect(); + this._pathElement.setAttribute("d", window.SVGPathSegList._pathSegArrayAsString(this._list)); + this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); + }; + + // When a path segment changes the list needs to be synchronized back to the path element. + window.SVGPathSegList.prototype.segmentChanged = function (pathSeg) { + this._writeListToPath(); + }; + + window.SVGPathSegList.prototype.clear = function () { + this._checkPathSynchronizedToList(); + + this._list.forEach(function (pathSeg) { + pathSeg._owningPathSegList = null; + }); + this._list = []; + this._writeListToPath(); + }; + + window.SVGPathSegList.prototype.initialize = function (newItem) { + this._checkPathSynchronizedToList(); + + this._list = [newItem]; + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + }; + + window.SVGPathSegList.prototype._checkValidIndex = function (index) { + if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw "INDEX_SIZE_ERR"; + }; + + window.SVGPathSegList.prototype.getItem = function (index) { + this._checkPathSynchronizedToList(); + + this._checkValidIndex(index); + return this._list[index]; + }; + + window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) { + this._checkPathSynchronizedToList(); + + // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. + if (index > this.numberOfItems) index = this.numberOfItems; + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._list.splice(index, 0, newItem); + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + }; + + window.SVGPathSegList.prototype.replaceItem = function (newItem, index) { + this._checkPathSynchronizedToList(); + + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._checkValidIndex(index); + this._list[index] = newItem; + newItem._owningPathSegList = this; + this._writeListToPath(); + return newItem; + }; + + window.SVGPathSegList.prototype.removeItem = function (index) { + this._checkPathSynchronizedToList(); + + this._checkValidIndex(index); + var item = this._list[index]; + this._list.splice(index, 1); + this._writeListToPath(); + return item; + }; + + window.SVGPathSegList.prototype.appendItem = function (newItem) { + this._checkPathSynchronizedToList(); + + if (newItem._owningPathSegList) { + // SVG2 spec says to make a copy. + newItem = newItem.clone(); + } + this._list.push(newItem); + newItem._owningPathSegList = this; + // TODO: Optimize this to just append to the existing attribute. + this._writeListToPath(); + return newItem; + }; + + window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) { + var string = ""; + var first = true; + pathSegArray.forEach(function (pathSeg) { + if (first) { + first = false; + string += pathSeg._asPathString(); + } else { + string += " " + pathSeg._asPathString(); + } + }); + return string; + }; + + // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. + window.SVGPathSegList.prototype._parsePath = function (string) { + if (!string || string.length == 0) return []; + + var owningPathSegList = this; + + var Builder = function Builder() { + this.pathSegList = []; + }; + + Builder.prototype.appendSegment = function (pathSeg) { + this.pathSegList.push(pathSeg); + }; + + var Source = function Source(string) { + this._string = string; + this._currentIndex = 0; + this._endIndex = this._string.length; + this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN; + + this._skipOptionalSpaces(); + }; + + Source.prototype._isCurrentSpace = function () { + var character = this._string[this._currentIndex]; + return character <= " " && (character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f"); + }; + + Source.prototype._skipOptionalSpaces = function () { + while (this._currentIndex < this._endIndex && this._isCurrentSpace()) { + this._currentIndex++; + }return this._currentIndex < this._endIndex; + }; + + Source.prototype._skipOptionalSpacesOrDelimiter = function () { + if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ",") return false; + if (this._skipOptionalSpaces()) { + if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ",") { + this._currentIndex++; + this._skipOptionalSpaces(); + } + } + return this._currentIndex < this._endIndex; + }; + + Source.prototype.hasMoreData = function () { + return this._currentIndex < this._endIndex; + }; + + Source.prototype.peekSegmentType = function () { + var lookahead = this._string[this._currentIndex]; + return this._pathSegTypeFromChar(lookahead); + }; + + Source.prototype._pathSegTypeFromChar = function (lookahead) { + switch (lookahead) { + case "Z": + case "z": + return window.SVGPathSeg.PATHSEG_CLOSEPATH; + case "M": + return window.SVGPathSeg.PATHSEG_MOVETO_ABS; + case "m": + return window.SVGPathSeg.PATHSEG_MOVETO_REL; + case "L": + return window.SVGPathSeg.PATHSEG_LINETO_ABS; + case "l": + return window.SVGPathSeg.PATHSEG_LINETO_REL; + case "C": + return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS; + case "c": + return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL; + case "Q": + return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS; + case "q": + return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL; + case "A": + return window.SVGPathSeg.PATHSEG_ARC_ABS; + case "a": + return window.SVGPathSeg.PATHSEG_ARC_REL; + case "H": + return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS; + case "h": + return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL; + case "V": + return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS; + case "v": + return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL; + case "S": + return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; + case "s": + return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL; + case "T": + return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; + case "t": + return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; + default: + return window.SVGPathSeg.PATHSEG_UNKNOWN; + } + }; + + Source.prototype._nextCommandHelper = function (lookahead, previousCommand) { + // Check for remaining coordinates in the current command. + if ((lookahead == "+" || lookahead == "-" || lookahead == "." || lookahead >= "0" && lookahead <= "9") && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) { + if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS) return window.SVGPathSeg.PATHSEG_LINETO_ABS; + if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL) return window.SVGPathSeg.PATHSEG_LINETO_REL; + return previousCommand; + } + return window.SVGPathSeg.PATHSEG_UNKNOWN; + }; + + Source.prototype.initialCommandIsMoveTo = function () { + // If the path is empty it is still valid, so return true. + if (!this.hasMoreData()) return true; + var command = this.peekSegmentType(); + // Path must start with moveTo. + return command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL; + }; + + // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. + // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF + Source.prototype._parseNumber = function () { + var exponent = 0; + var integer = 0; + var frac = 1; + var decimal = 0; + var sign = 1; + var expsign = 1; + + var startIndex = this._currentIndex; + + this._skipOptionalSpaces(); + + // Read the sign. + if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "+") this._currentIndex++;else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "-") { + this._currentIndex++; + sign = -1; + } + + if (this._currentIndex == this._endIndex || (this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") && this._string.charAt(this._currentIndex) != ".") + // The first character of a number must be one of [0-9+-.]. + return undefined; + + // Read the integer part, build right-to-left. + var startIntPartIndex = this._currentIndex; + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") { + this._currentIndex++; + } // Advance to first non-digit. + + if (this._currentIndex != startIntPartIndex) { + var scanIntPartIndex = this._currentIndex - 1; + var multiplier = 1; + while (scanIntPartIndex >= startIntPartIndex) { + integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0"); + multiplier *= 10; + } + } + + // Read the decimals. + if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") { + this._currentIndex++; + + // There must be a least one digit following the . + if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") return undefined; + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") { + frac *= 10; + decimal += (this._string.charAt(this._currentIndex) - "0") / frac; + this._currentIndex += 1; + } + } + + // Read the exponent part. + if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == "e" || this._string.charAt(this._currentIndex) == "E") && this._string.charAt(this._currentIndex + 1) != "x" && this._string.charAt(this._currentIndex + 1) != "m") { + this._currentIndex++; + + // Read the sign of the exponent. + if (this._string.charAt(this._currentIndex) == "+") { + this._currentIndex++; + } else if (this._string.charAt(this._currentIndex) == "-") { + this._currentIndex++; + expsign = -1; + } + + // There must be an exponent. + if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") return undefined; + + while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") { + exponent *= 10; + exponent += this._string.charAt(this._currentIndex) - "0"; + this._currentIndex++; + } + } + + var number = integer + decimal; + number *= sign; + + if (exponent) number *= Math.pow(10, expsign * exponent); + + if (startIndex == this._currentIndex) return undefined; + + this._skipOptionalSpacesOrDelimiter(); + + return number; + }; + + Source.prototype._parseArcFlag = function () { + if (this._currentIndex >= this._endIndex) return undefined; + var flag = false; + var flagChar = this._string.charAt(this._currentIndex++); + if (flagChar == "0") flag = false;else if (flagChar == "1") flag = true;else return undefined; + + this._skipOptionalSpacesOrDelimiter(); + return flag; + }; + + Source.prototype.parseSegment = function () { + var lookahead = this._string[this._currentIndex]; + var command = this._pathSegTypeFromChar(lookahead); + if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) { + // Possibly an implicit command. Not allowed if this is the first command. + if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN) return null; + command = this._nextCommandHelper(lookahead, this._previousCommand); + if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) return null; + } else { + this._currentIndex++; + } + + this._previousCommand = command; + + switch (command) { + case window.SVGPathSeg.PATHSEG_MOVETO_REL: + return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_MOVETO_ABS: + return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_REL: + return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_ABS: + return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: + return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: + return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: + return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber()); + case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: + return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber()); + case window.SVGPathSeg.PATHSEG_CLOSEPATH: + this._skipOptionalSpaces(); + return new window.SVGPathSegClosePath(owningPathSegList); + case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: + var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; + return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); + case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: + var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; + return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); + case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; + return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2); + case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; + return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2); + case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: + var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; + return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1); + case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: + var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; + return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1); + case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); + case window.SVGPathSeg.PATHSEG_ARC_REL: + var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; + return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); + case window.SVGPathSeg.PATHSEG_ARC_ABS: + var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; + return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); + default: + throw "Unknown path seg type."; + } + }; + + var builder = new Builder(); + var source = new Source(string); + + if (!source.initialCommandIsMoveTo()) return []; + while (source.hasMoreData()) { + var pathSeg = source.parseSegment(); + if (!pathSeg) return []; + builder.appendSegment(pathSeg); + } + + return builder.pathSegList; + }; + } +})(); + +/* jshint ignore:end */ + +c3_chart_fn.axis = function () {}; +c3_chart_fn.axis.labels = function (labels) { + var $$ = this.internal; + if (arguments.length) { + Object.keys(labels).forEach(function (axisId) { + $$.axis.setLabelText(axisId, labels[axisId]); + }); + $$.axis.updateLabels(); + } + // TODO: return some values? +}; +c3_chart_fn.axis.max = function (max) { + var $$ = this.internal, + config = $$.config; + if (arguments.length) { + if ((typeof max === 'undefined' ? 'undefined' : _typeof(max)) === 'object') { + if (isValue(max.x)) { + config.axis_x_max = max.x; + } + if (isValue(max.y)) { + config.axis_y_max = max.y; + } + if (isValue(max.y2)) { + config.axis_y2_max = max.y2; + } + } else { + config.axis_y_max = config.axis_y2_max = max; + } + $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); + } else { + return { + x: config.axis_x_max, + y: config.axis_y_max, + y2: config.axis_y2_max + }; + } +}; +c3_chart_fn.axis.min = function (min) { + var $$ = this.internal, + config = $$.config; + if (arguments.length) { + if ((typeof min === 'undefined' ? 'undefined' : _typeof(min)) === 'object') { + if (isValue(min.x)) { + config.axis_x_min = min.x; + } + if (isValue(min.y)) { + config.axis_y_min = min.y; + } + if (isValue(min.y2)) { + config.axis_y2_min = min.y2; + } + } else { + config.axis_y_min = config.axis_y2_min = min; + } + $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); + } else { + return { + x: config.axis_x_min, + y: config.axis_y_min, + y2: config.axis_y2_min + }; + } +}; +c3_chart_fn.axis.range = function (range) { + if (arguments.length) { + if (isDefined(range.max)) { + this.axis.max(range.max); + } + if (isDefined(range.min)) { + this.axis.min(range.min); + } + } else { + return { + max: this.axis.max(), + min: this.axis.min() + }; + } +}; + +c3_chart_fn.category = function (i, category) { + var $$ = this.internal, + config = $$.config; + if (arguments.length > 1) { + config.axis_x_categories[i] = category; + $$.redraw(); + } + return config.axis_x_categories[i]; +}; +c3_chart_fn.categories = function (categories) { + var $$ = this.internal, + config = $$.config; + if (!arguments.length) { + return config.axis_x_categories; + } + config.axis_x_categories = categories; + $$.redraw(); + return config.axis_x_categories; +}; + +c3_chart_fn.resize = function (size) { + var $$ = this.internal, + config = $$.config; + config.size_width = size ? size.width : null; + config.size_height = size ? size.height : null; + this.flush(); +}; + +c3_chart_fn.flush = function () { + var $$ = this.internal; + $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false }); +}; + +c3_chart_fn.destroy = function () { + var $$ = this.internal; + + window.clearInterval($$.intervalForObserveInserted); + + if ($$.resizeTimeout !== undefined) { + window.clearTimeout($$.resizeTimeout); + } + + if (window.detachEvent) { + window.detachEvent('onresize', $$.resizeFunction); + } else if (window.removeEventListener) { + window.removeEventListener('resize', $$.resizeFunction); + } else { + var wrapper = window.onresize; + // check if no one else removed our wrapper and remove our resizeFunction from it + if (wrapper && wrapper.add && wrapper.remove) { + wrapper.remove($$.resizeFunction); + } + } + + $$.selectChart.classed('c3', false).html(""); + + // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen. + Object.keys($$).forEach(function (key) { + $$[key] = null; + }); + + return null; +}; + +// TODO: fix +c3_chart_fn.color = function (id) { + var $$ = this.internal; + return $$.color(id); // more patterns +}; + +c3_chart_fn.data = function (targetIds) { + var targets = this.internal.data.targets; + return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) { + return [].concat(targetIds).indexOf(t.id) >= 0; + }); +}; +c3_chart_fn.data.shown = function (targetIds) { + return this.internal.filterTargetsToShow(this.data(targetIds)); +}; +c3_chart_fn.data.values = function (targetId) { + var targets, + values = null; + if (targetId) { + targets = this.data(targetId); + values = targets[0] ? targets[0].values.map(function (d) { + return d.value; + }) : null; + } + return values; +}; +c3_chart_fn.data.names = function (names) { + this.internal.clearLegendItemTextBoxCache(); + return this.internal.updateDataAttributes('names', names); +}; +c3_chart_fn.data.colors = function (colors) { + return this.internal.updateDataAttributes('colors', colors); +}; +c3_chart_fn.data.axes = function (axes) { + return this.internal.updateDataAttributes('axes', axes); +}; + +c3_chart_fn.flow = function (args) { + var $$ = this.internal, + targets, + data, + notfoundIds = [], + orgDataCount = $$.getMaxDataCount(), + dataCount, + domain, + baseTarget, + baseValue, + length = 0, + tail = 0, + diff, + to; + + if (args.json) { + data = $$.convertJsonToData(args.json, args.keys); + } else if (args.rows) { + data = $$.convertRowsToData(args.rows); + } else if (args.columns) { + data = $$.convertColumnsToData(args.columns); + } else { + return; + } + targets = $$.convertDataToTargets(data, true); + + // Update/Add data + $$.data.targets.forEach(function (t) { + var found = false, + i, + j; + for (i = 0; i < targets.length; i++) { + if (t.id === targets[i].id) { + found = true; + + if (t.values[t.values.length - 1]) { + tail = t.values[t.values.length - 1].index + 1; + } + length = targets[i].values.length; + + for (j = 0; j < length; j++) { + targets[i].values[j].index = tail + j; + if (!$$.isTimeSeries()) { + targets[i].values[j].x = tail + j; + } + } + t.values = t.values.concat(targets[i].values); + + targets.splice(i, 1); + break; + } + } + if (!found) { + notfoundIds.push(t.id); + } + }); + + // Append null for not found targets + $$.data.targets.forEach(function (t) { + var i, j; + for (i = 0; i < notfoundIds.length; i++) { + if (t.id === notfoundIds[i]) { + tail = t.values[t.values.length - 1].index + 1; + for (j = 0; j < length; j++) { + t.values.push({ + id: t.id, + index: tail + j, + x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, + value: null + }); + } + } + } + }); + + // Generate null values for new target + if ($$.data.targets.length) { + targets.forEach(function (t) { + var i, + missing = []; + for (i = $$.data.targets[0].values[0].index; i < tail; i++) { + missing.push({ + id: t.id, + index: i, + x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, + value: null + }); + } + t.values.forEach(function (v) { + v.index += tail; + if (!$$.isTimeSeries()) { + v.x += tail; + } + }); + t.values = missing.concat(t.values); + }); + } + $$.data.targets = $$.data.targets.concat(targets); // add remained + + // check data count because behavior needs to change when it's only one + dataCount = $$.getMaxDataCount(); + baseTarget = $$.data.targets[0]; + baseValue = baseTarget.values[0]; + + // Update length to flow if needed + if (isDefined(args.to)) { + length = 0; + to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; + baseTarget.values.forEach(function (v) { + if (v.x < to) { + length++; + } + }); + } else if (isDefined(args.length)) { + length = args.length; + } + + // If only one data, update the domain to flow from left edge of the chart + if (!orgDataCount) { + if ($$.isTimeSeries()) { + if (baseTarget.values.length > 1) { + diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; + } else { + diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; + } + } else { + diff = 1; + } + domain = [baseValue.x - diff, baseValue.x]; + $$.updateXDomain(null, true, true, false, domain); + } else if (orgDataCount === 1) { + if ($$.isTimeSeries()) { + diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; + domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; + $$.updateXDomain(null, true, true, false, domain); + } + } + + // Set targets + $$.updateTargets($$.data.targets); + + // Redraw with new targets + $$.redraw({ + flow: { + index: baseValue.index, + length: length, + duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, + done: args.done, + orgDataCount: orgDataCount + }, + withLegend: true, + withTransition: orgDataCount > 1, + withTrimXDomain: false, + withUpdateXAxis: true + }); +}; + +c3_chart_internal_fn.generateFlow = function (args) { + var $$ = this, + config = $$.config, + d3 = $$.d3; + + return function () { + var targets = args.targets, + flow = args.flow, + drawBar = args.drawBar, + drawLine = args.drawLine, + drawArea = args.drawArea, + cx = args.cx, + cy = args.cy, + xv = args.xv, + xForText = args.xForText, + yForText = args.yForText, + duration = args.duration; + + var translateX, + scaleX = 1, + transform, + flowIndex = flow.index, + flowLength = flow.length, + flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), + flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength), + orgDomain = $$.x.domain(), + domain, + durationForFlow = flow.duration || duration, + done = flow.done || function () {}, + wait = $$.generateWait(); + + var xgrid = $$.xgrid || d3.selectAll([]), + xgridLines = $$.xgridLines || d3.selectAll([]), + mainRegion = $$.mainRegion || d3.selectAll([]), + mainText = $$.mainText || d3.selectAll([]), + mainBar = $$.mainBar || d3.selectAll([]), + mainLine = $$.mainLine || d3.selectAll([]), + mainArea = $$.mainArea || d3.selectAll([]), + mainCircle = $$.mainCircle || d3.selectAll([]); + + // set flag + $$.flowing = true; + + // remove head data after rendered + $$.data.targets.forEach(function (d) { + d.values.splice(0, flowLength); + }); + + // update x domain to generate axis elements for flow + domain = $$.updateXDomain(targets, true, true); + // update elements related to x scale + if ($$.updateXGrid) { + $$.updateXGrid(true); + } + + // generate transform to flow + if (!flow.orgDataCount) { + // if empty + if ($$.data.targets[0].values.length !== 1) { + translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); + } else { + if ($$.isTimeSeries()) { + flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0); + flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1); + translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); + } else { + translateX = diffDomain(domain) / 2; + } + } + } else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) { + translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); + } else { + if ($$.isTimeSeries()) { + translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); + } else { + translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); + } + } + scaleX = diffDomain(orgDomain) / diffDomain(domain); + transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)'; + + $$.hideXGridFocus(); + + d3.transition().ease('linear').duration(durationForFlow).each(function () { + wait.add($$.axes.x.transition().call($$.xAxis)); + wait.add(mainBar.transition().attr('transform', transform)); + wait.add(mainLine.transition().attr('transform', transform)); + wait.add(mainArea.transition().attr('transform', transform)); + wait.add(mainCircle.transition().attr('transform', transform)); + wait.add(mainText.transition().attr('transform', transform)); + wait.add(mainRegion.filter($$.isRegionOnX).transition().attr('transform', transform)); + wait.add(xgrid.transition().attr('transform', transform)); + wait.add(xgridLines.transition().attr('transform', transform)); + }).call(wait, function () { + var i, + shapes = [], + texts = [], + eventRects = []; + + // remove flowed elements + if (flowLength) { + for (i = 0; i < flowLength; i++) { + shapes.push('.' + CLASS.shape + '-' + (flowIndex + i)); + texts.push('.' + CLASS.text + '-' + (flowIndex + i)); + eventRects.push('.' + CLASS.eventRect + '-' + (flowIndex + i)); + } + $$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove(); + $$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove(); + $$.svg.selectAll('.' + CLASS.eventRects).selectAll(eventRects).remove(); + $$.svg.select('.' + CLASS.xgrid).remove(); + } + + // draw again for removing flowed elements and reverting attr + xgrid.attr('transform', null).attr($$.xgridAttr); + xgridLines.attr('transform', null); + xgridLines.select('line').attr("x1", config.axis_rotated ? 0 : xv).attr("x2", config.axis_rotated ? $$.width : xv); + xgridLines.select('text').attr("x", config.axis_rotated ? $$.width : 0).attr("y", xv); + mainBar.attr('transform', null).attr("d", drawBar); + mainLine.attr('transform', null).attr("d", drawLine); + mainArea.attr('transform', null).attr("d", drawArea); + mainCircle.attr('transform', null).attr("cx", cx).attr("cy", cy); + mainText.attr('transform', null).attr('x', xForText).attr('y', yForText).style('fill-opacity', $$.opacityForText.bind($$)); + mainRegion.attr('transform', null); + mainRegion.select('rect').filter($$.isRegionOnX).attr("x", $$.regionX.bind($$)).attr("width", $$.regionWidth.bind($$)); + + if (config.interaction_enabled) { + $$.redrawEventRect(); + } + + // callback for end of flow + done(); + + $$.flowing = false; + }); + }; +}; + +c3_chart_fn.focus = function (targetIds) { + var $$ = this.internal, + candidates; + + targetIds = $$.mapToTargetIds(targetIds); + candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), this.revert(); + this.defocus(); + candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); + if ($$.hasArcType()) { + $$.expandArc(targetIds); + } + $$.toggleFocusLegend(targetIds, true); + + $$.focusedTargetIds = targetIds; + $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) { + return targetIds.indexOf(id) < 0; + }); +}; + +c3_chart_fn.defocus = function (targetIds) { + var $$ = this.internal, + candidates; + + targetIds = $$.mapToTargetIds(targetIds); + candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); + if ($$.hasArcType()) { + $$.unexpandArc(targetIds); + } + $$.toggleFocusLegend(targetIds, false); + + $$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) { + return targetIds.indexOf(id) < 0; + }); + $$.defocusedTargetIds = targetIds; +}; + +c3_chart_fn.revert = function (targetIds) { + var $$ = this.internal, + candidates; + + targetIds = $$.mapToTargetIds(targetIds); + candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets + + candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); + if ($$.hasArcType()) { + $$.unexpandArc(targetIds); + } + if ($$.config.legend_show) { + $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); + $$.legend.selectAll($$.selectorLegends(targetIds)).filter(function () { + return $$.d3.select(this).classed(CLASS.legendItemFocused); + }).classed(CLASS.legendItemFocused, false); + } + + $$.focusedTargetIds = []; + $$.defocusedTargetIds = []; +}; + +c3_chart_fn.xgrids = function (grids) { + var $$ = this.internal, + config = $$.config; + if (!grids) { + return config.grid_x_lines; + } + config.grid_x_lines = grids; + $$.redrawWithoutRescale(); + return config.grid_x_lines; +}; +c3_chart_fn.xgrids.add = function (grids) { + var $$ = this.internal; + return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])); +}; +c3_chart_fn.xgrids.remove = function (params) { + // TODO: multiple + var $$ = this.internal; + $$.removeGridLines(params, true); +}; + +c3_chart_fn.ygrids = function (grids) { + var $$ = this.internal, + config = $$.config; + if (!grids) { + return config.grid_y_lines; + } + config.grid_y_lines = grids; + $$.redrawWithoutRescale(); + return config.grid_y_lines; +}; +c3_chart_fn.ygrids.add = function (grids) { + var $$ = this.internal; + return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])); +}; +c3_chart_fn.ygrids.remove = function (params) { + // TODO: multiple + var $$ = this.internal; + $$.removeGridLines(params, false); +}; + +c3_chart_fn.groups = function (groups) { + var $$ = this.internal, + config = $$.config; + if (isUndefined(groups)) { + return config.data_groups; + } + config.data_groups = groups; + $$.redraw(); + return config.data_groups; +}; + +c3_chart_fn.legend = function () {}; +c3_chart_fn.legend.show = function (targetIds) { + var $$ = this.internal; + $$.showLegend($$.mapToTargetIds(targetIds)); + $$.updateAndRedraw({ withLegend: true }); +}; +c3_chart_fn.legend.hide = function (targetIds) { + var $$ = this.internal; + $$.hideLegend($$.mapToTargetIds(targetIds)); + $$.updateAndRedraw({ withLegend: true }); +}; + +c3_chart_fn.load = function (args) { + var $$ = this.internal, + config = $$.config; + // update xs if specified + if (args.xs) { + $$.addXs(args.xs); + } + // update names if exists + if ('names' in args) { + c3_chart_fn.data.names.bind(this)(args.names); + } + // update classes if exists + if ('classes' in args) { + Object.keys(args.classes).forEach(function (id) { + config.data_classes[id] = args.classes[id]; + }); + } + // update categories if exists + if ('categories' in args && $$.isCategorized()) { + config.axis_x_categories = args.categories; + } + // update axes if exists + if ('axes' in args) { + Object.keys(args.axes).forEach(function (id) { + config.data_axes[id] = args.axes[id]; + }); + } + // update colors if exists + if ('colors' in args) { + Object.keys(args.colors).forEach(function (id) { + config.data_colors[id] = args.colors[id]; + }); + } + // use cache if exists + if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { + $$.load($$.getCaches(args.cacheIds), args.done); + return; + } + // unload if needed + if ('unload' in args) { + // TODO: do not unload if target will load (included in url/rows/columns) + $$.unload($$.mapToTargetIds(typeof args.unload === 'boolean' && args.unload ? null : args.unload), function () { + $$.loadFromArgs(args); + }); + } else { + $$.loadFromArgs(args); + } +}; + +c3_chart_fn.unload = function (args) { + var $$ = this.internal; + args = args || {}; + if (args instanceof Array) { + args = { ids: args }; + } else if (typeof args === 'string') { + args = { ids: [args] }; + } + $$.unload($$.mapToTargetIds(args.ids), function () { + $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); + if (args.done) { + args.done(); + } + }); +}; + +c3_chart_fn.regions = function (regions) { + var $$ = this.internal, + config = $$.config; + if (!regions) { + return config.regions; + } + config.regions = regions; + $$.redrawWithoutRescale(); + return config.regions; +}; +c3_chart_fn.regions.add = function (regions) { + var $$ = this.internal, + config = $$.config; + if (!regions) { + return config.regions; + } + config.regions = config.regions.concat(regions); + $$.redrawWithoutRescale(); + return config.regions; +}; +c3_chart_fn.regions.remove = function (options) { + var $$ = this.internal, + config = $$.config, + duration, + classes, + regions; + + options = options || {}; + duration = $$.getOption(options, "duration", config.transition_duration); + classes = $$.getOption(options, "classes", [CLASS.region]); + + regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) { + return '.' + c; + })); + (duration ? regions.transition().duration(duration) : regions).style('opacity', 0).remove(); + + config.regions = config.regions.filter(function (region) { + var found = false; + if (!region['class']) { + return true; + } + region['class'].split(' ').forEach(function (c) { + if (classes.indexOf(c) >= 0) { + found = true; + } + }); + return !found; + }); + + return config.regions; +}; + +c3_chart_fn.selected = function (targetId) { + var $$ = this.internal, + d3 = $$.d3; + return d3.merge($$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape).filter(function () { + return d3.select(this).classed(CLASS.SELECTED); + }).map(function (d) { + return d.map(function (d) { + var data = d.__data__;return data.data ? data.data : data; + }); + })); +}; +c3_chart_fn.select = function (ids, indices, resetOther) { + var $$ = this.internal, + d3 = $$.d3, + config = $$.config; + if (!config.data_selection_enabled) { + return; + } + $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { + var shape = d3.select(this), + id = d.data ? d.data.id : d.id, + toggle = $$.getToggle(this, d).bind($$), + isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, + isTargetIndex = !indices || indices.indexOf(i) >= 0, + isSelected = shape.classed(CLASS.SELECTED); + // line/area selection not supported yet + if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { + return; + } + if (isTargetId && isTargetIndex) { + if (config.data_selection_isselectable(d) && !isSelected) { + toggle(true, shape.classed(CLASS.SELECTED, true), d, i); + } + } else if (isDefined(resetOther) && resetOther) { + if (isSelected) { + toggle(false, shape.classed(CLASS.SELECTED, false), d, i); + } + } + }); +}; +c3_chart_fn.unselect = function (ids, indices) { + var $$ = this.internal, + d3 = $$.d3, + config = $$.config; + if (!config.data_selection_enabled) { + return; + } + $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) { + var shape = d3.select(this), + id = d.data ? d.data.id : d.id, + toggle = $$.getToggle(this, d).bind($$), + isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, + isTargetIndex = !indices || indices.indexOf(i) >= 0, + isSelected = shape.classed(CLASS.SELECTED); + // line/area selection not supported yet + if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { + return; + } + if (isTargetId && isTargetIndex) { + if (config.data_selection_isselectable(d)) { + if (isSelected) { + toggle(false, shape.classed(CLASS.SELECTED, false), d, i); + } + } + } + }); +}; + +c3_chart_fn.show = function (targetIds, options) { + var $$ = this.internal, + targets; + + targetIds = $$.mapToTargetIds(targetIds); + options = options || {}; + + $$.removeHiddenTargetIds(targetIds); + targets = $$.svg.selectAll($$.selectorTargets(targetIds)); + + targets.transition().style('opacity', 1, 'important').call($$.endall, function () { + targets.style('opacity', null).style('opacity', 1); + }); + + if (options.withLegend) { + $$.showLegend(targetIds); + } + + $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); +}; + +c3_chart_fn.hide = function (targetIds, options) { + var $$ = this.internal, + targets; + + targetIds = $$.mapToTargetIds(targetIds); + options = options || {}; + + $$.addHiddenTargetIds(targetIds); + targets = $$.svg.selectAll($$.selectorTargets(targetIds)); + + targets.transition().style('opacity', 0, 'important').call($$.endall, function () { + targets.style('opacity', null).style('opacity', 0); + }); + + if (options.withLegend) { + $$.hideLegend(targetIds); + } + + $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); +}; + +c3_chart_fn.toggle = function (targetIds, options) { + var that = this, + $$ = this.internal; + $$.mapToTargetIds(targetIds).forEach(function (targetId) { + $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); + }); +}; + +c3_chart_fn.tooltip = function () {}; +c3_chart_fn.tooltip.show = function (args) { + var $$ = this.internal, + index, + mouse; + + // determine mouse position on the chart + if (args.mouse) { + mouse = args.mouse; + } + + // determine focus data + if (args.data) { + if ($$.isMultipleX()) { + // if multiple xs, target point will be determined by mouse + mouse = [$$.x(args.data.x), $$.getYScale(args.data.id)(args.data.value)]; + index = null; + } else { + // TODO: when tooltip_grouped = false + index = isValue(args.data.index) ? args.data.index : $$.getIndexByX(args.data.x); + } + } else if (typeof args.x !== 'undefined') { + index = $$.getIndexByX(args.x); + } else if (typeof args.index !== 'undefined') { + index = args.index; + } + + // emulate mouse events to show + $$.dispatchEvent('mouseover', index, mouse); + $$.dispatchEvent('mousemove', index, mouse); + + $$.config.tooltip_onshow.call($$, args.data); +}; +c3_chart_fn.tooltip.hide = function () { + // TODO: get target data by checking the state of focus + this.internal.dispatchEvent('mouseout', 0); + + this.internal.config.tooltip_onhide.call(this); +}; + +c3_chart_fn.transform = function (type, targetIds) { + var $$ = this.internal, + options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; + $$.transformTo(targetIds, type, options); +}; + +c3_chart_internal_fn.transformTo = function (targetIds, type, optionsForRedraw) { + var $$ = this, + withTransitionForAxis = !$$.hasArcType(), + options = optionsForRedraw || { withTransitionForAxis: withTransitionForAxis }; + options.withTransitionForTransform = false; + $$.transiting = false; + $$.setTargetType(targetIds, type); + $$.updateTargets($$.data.targets); // this is needed when transforming to arc + $$.updateAndRedraw(options); +}; + +c3_chart_fn.x = function (x) { + var $$ = this.internal; + if (arguments.length) { + $$.updateTargetX($$.data.targets, x); + $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); + } + return $$.data.xs; +}; +c3_chart_fn.xs = function (xs) { + var $$ = this.internal; + if (arguments.length) { + $$.updateTargetXs($$.data.targets, xs); + $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); + } + return $$.data.xs; +}; + +c3_chart_fn.zoom = function (domain) { + var $$ = this.internal; + if (domain) { + if ($$.isTimeSeries()) { + domain = domain.map(function (x) { + return $$.parseDate(x); + }); + } + $$.brush.extent(domain); + $$.redraw({ withUpdateXDomain: true, withY: $$.config.zoom_rescale }); + $$.config.zoom_onzoom.call(this, $$.x.orgDomain()); + } + return $$.brush.extent(); +}; +c3_chart_fn.zoom.enable = function (enabled) { + var $$ = this.internal; + $$.config.zoom_enabled = enabled; + $$.updateAndRedraw(); +}; +c3_chart_fn.unzoom = function () { + var $$ = this.internal; + $$.brush.clear().update(); + $$.redraw({ withUpdateXDomain: true }); +}; + +c3_chart_fn.zoom.max = function (max) { + var $$ = this.internal, + config = $$.config, + d3 = $$.d3; + if (max === 0 || max) { + config.zoom_x_max = d3.max([$$.orgXDomain[1], max]); + } else { + return config.zoom_x_max; + } +}; + +c3_chart_fn.zoom.min = function (min) { + var $$ = this.internal, + config = $$.config, + d3 = $$.d3; + if (min === 0 || min) { + config.zoom_x_min = d3.min([$$.orgXDomain[0], min]); + } else { + return config.zoom_x_min; + } +}; + +c3_chart_fn.zoom.range = function (range) { + if (arguments.length) { + if (isDefined(range.max)) { + this.domain.max(range.max); + } + if (isDefined(range.min)) { + this.domain.min(range.min); + } + } else { + return { + max: this.domain.max(), + min: this.domain.min() + }; + } +}; + +c3_chart_internal_fn.initPie = function () { + var $$ = this, + d3 = $$.d3; + $$.pie = d3.layout.pie().value(function (d) { + return d.values.reduce(function (a, b) { + return a + b.value; + }, 0); + }); + $$.pie.sort($$.getOrderFunction() || null); +}; + +c3_chart_internal_fn.updateRadius = function () { + var $$ = this, + config = $$.config, + w = config.gauge_width || config.donut_width; + $$.radiusExpanded = Math.min($$.arcWidth, $$.arcHeight) / 2; + $$.radius = $$.radiusExpanded * 0.95; + $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6; + $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0; +}; + +c3_chart_internal_fn.updateArc = function () { + var $$ = this; + $$.svgArc = $$.getSvgArc(); + $$.svgArcExpanded = $$.getSvgArcExpanded(); + $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98); +}; + +c3_chart_internal_fn.updateAngle = function (d) { + var $$ = this, + config = $$.config, + found = false, + index = 0, + gMin, + gMax, + gTic, + gValue; + + if (!config) { + return null; + } + + $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) { + if (!found && t.data.id === d.data.id) { + found = true; + d = t; + d.index = index; + } + index++; + }); + if (isNaN(d.startAngle)) { + d.startAngle = 0; + } + if (isNaN(d.endAngle)) { + d.endAngle = d.startAngle; + } + if ($$.isGaugeType(d.data)) { + gMin = config.gauge_min; + gMax = config.gauge_max; + gTic = Math.PI * (config.gauge_fullCircle ? 2 : 1) / (gMax - gMin); + gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : gMax - gMin; + d.startAngle = config.gauge_startingAngle; + d.endAngle = d.startAngle + gTic * gValue; + } + return found ? d : null; +}; + +c3_chart_internal_fn.getSvgArc = function () { + var $$ = this, + arc = $$.d3.svg.arc().outerRadius($$.radius).innerRadius($$.innerRadius), + newArc = function newArc(d, withoutUpdate) { + var updated; + if (withoutUpdate) { + return arc(d); + } // for interpolate + updated = $$.updateAngle(d); + return updated ? arc(updated) : "M 0 0"; + }; + // TODO: extends all function + newArc.centroid = arc.centroid; + return newArc; +}; + +c3_chart_internal_fn.getSvgArcExpanded = function (rate) { + var $$ = this, + arc = $$.d3.svg.arc().outerRadius($$.radiusExpanded * (rate ? rate : 1)).innerRadius($$.innerRadius); + return function (d) { + var updated = $$.updateAngle(d); + return updated ? arc(updated) : "M 0 0"; + }; +}; + +c3_chart_internal_fn.getArc = function (d, withoutUpdate, force) { + return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : "M 0 0"; +}; + +c3_chart_internal_fn.transformForArcLabel = function (d) { + var $$ = this, + config = $$.config, + updated = $$.updateAngle(d), + c, + x, + y, + h, + ratio, + translate = ""; + if (updated && !$$.hasType('gauge')) { + c = this.svgArc.centroid(updated); + x = isNaN(c[0]) ? 0 : c[0]; + y = isNaN(c[1]) ? 0 : c[1]; + h = Math.sqrt(x * x + y * y); + if ($$.hasType('donut') && config.donut_label_ratio) { + ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio; + } else if ($$.hasType('pie') && config.pie_label_ratio) { + ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio; + } else { + ratio = $$.radius && h ? (36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius / h : 0; + } + translate = "translate(" + x * ratio + ',' + y * ratio + ")"; + } + return translate; +}; + +c3_chart_internal_fn.getArcRatio = function (d) { + var $$ = this, + config = $$.config, + whole = Math.PI * ($$.hasType('gauge') && !config.gauge_fullCircle ? 1 : 2); + return d ? (d.endAngle - d.startAngle) / whole : null; +}; + +c3_chart_internal_fn.convertToArcData = function (d) { + return this.addName({ + id: d.data.id, + value: d.value, + ratio: this.getArcRatio(d), + index: d.index + }); +}; + +c3_chart_internal_fn.textForArcLabel = function (d) { + var $$ = this, + updated, + value, + ratio, + id, + format; + if (!$$.shouldShowArcLabel()) { + return ""; + } + updated = $$.updateAngle(d); + value = updated ? updated.value : null; + ratio = $$.getArcRatio(updated); + id = d.data.id; + if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) { + return ""; + } + format = $$.getArcLabelFormat(); + return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio); +}; + +c3_chart_internal_fn.textForGaugeMinMax = function (value, isMax) { + var $$ = this, + format = $$.getGaugeLabelExtents(); + + return format ? format(value, isMax) : value; +}; + +c3_chart_internal_fn.expandArc = function (targetIds) { + var $$ = this, + interval; + + // MEMO: avoid to cancel transition + if ($$.transiting) { + interval = window.setInterval(function () { + if (!$$.transiting) { + window.clearInterval(interval); + if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) { + $$.expandArc(targetIds); + } + } + }, 10); + return; + } + + targetIds = $$.mapToTargetIds(targetIds); + + $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) { + if (!$$.shouldExpand(d.data.id)) { + return; + } + $$.d3.select(this).selectAll('path').transition().duration($$.expandDuration(d.data.id)).attr("d", $$.svgArcExpanded).transition().duration($$.expandDuration(d.data.id) * 2).attr("d", $$.svgArcExpandedSub).each(function (d) { + if ($$.isDonutType(d.data)) { + // callback here + } + }); + }); +}; + +c3_chart_internal_fn.unexpandArc = function (targetIds) { + var $$ = this; + + if ($$.transiting) { + return; + } + + targetIds = $$.mapToTargetIds(targetIds); + + $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).selectAll('path').transition().duration(function (d) { + return $$.expandDuration(d.data.id); + }).attr("d", $$.svgArc); + $$.svg.selectAll('.' + CLASS.arc); +}; + +c3_chart_internal_fn.expandDuration = function (id) { + var $$ = this, + config = $$.config; + + if ($$.isDonutType(id)) { + return config.donut_expand_duration; + } else if ($$.isGaugeType(id)) { + return config.gauge_expand_duration; + } else if ($$.isPieType(id)) { + return config.pie_expand_duration; + } else { + return 50; + } +}; + +c3_chart_internal_fn.shouldExpand = function (id) { + var $$ = this, + config = $$.config; + return $$.isDonutType(id) && config.donut_expand || $$.isGaugeType(id) && config.gauge_expand || $$.isPieType(id) && config.pie_expand; +}; + +c3_chart_internal_fn.shouldShowArcLabel = function () { + var $$ = this, + config = $$.config, + shouldShow = true; + if ($$.hasType('donut')) { + shouldShow = config.donut_label_show; + } else if ($$.hasType('pie')) { + shouldShow = config.pie_label_show; + } + // when gauge, always true + return shouldShow; +}; + +c3_chart_internal_fn.meetsArcLabelThreshold = function (ratio) { + var $$ = this, + config = $$.config, + threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold; + return ratio >= threshold; +}; + +c3_chart_internal_fn.getArcLabelFormat = function () { + var $$ = this, + config = $$.config, + format = config.pie_label_format; + if ($$.hasType('gauge')) { + format = config.gauge_label_format; + } else if ($$.hasType('donut')) { + format = config.donut_label_format; + } + return format; +}; + +c3_chart_internal_fn.getGaugeLabelExtents = function () { + var $$ = this, + config = $$.config; + return config.gauge_label_extents; +}; + +c3_chart_internal_fn.getArcTitle = function () { + var $$ = this; + return $$.hasType('donut') ? $$.config.donut_title : ""; +}; + +c3_chart_internal_fn.updateTargetsForArc = function (targets) { + var $$ = this, + main = $$.main, + mainPieUpdate, + mainPieEnter, + classChartArc = $$.classChartArc.bind($$), + classArcs = $$.classArcs.bind($$), + classFocus = $$.classFocus.bind($$); + mainPieUpdate = main.select('.' + CLASS.chartArcs).selectAll('.' + CLASS.chartArc).data($$.pie(targets)).attr("class", function (d) { + return classChartArc(d) + classFocus(d.data); + }); + mainPieEnter = mainPieUpdate.enter().append("g").attr("class", classChartArc); + mainPieEnter.append('g').attr('class', classArcs); + mainPieEnter.append("text").attr("dy", $$.hasType('gauge') ? "-.1em" : ".35em").style("opacity", 0).style("text-anchor", "middle").style("pointer-events", "none"); + // MEMO: can not keep same color..., but not bad to update color in redraw + //mainPieUpdate.exit().remove(); +}; + +c3_chart_internal_fn.initArc = function () { + var $$ = this; + $$.arcs = $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartArcs).attr("transform", $$.getTranslate('arc')); + $$.arcs.append('text').attr('class', CLASS.chartArcsTitle).style("text-anchor", "middle").text($$.getArcTitle()); +}; + +c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransform) { + var $$ = this, + d3 = $$.d3, + config = $$.config, + main = $$.main, + mainArc; + mainArc = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arc).data($$.arcData.bind($$)); + mainArc.enter().append('path').attr("class", $$.classArc.bind($$)).style("fill", function (d) { + return $$.color(d.data); + }).style("cursor", function (d) { + return config.interaction_enabled && config.data_selection_isselectable(d) ? "pointer" : null; + }).each(function (d) { + if ($$.isGaugeType(d.data)) { + d.startAngle = d.endAngle = config.gauge_startingAngle; + } + this._current = d; + }); + mainArc.attr("transform", function (d) { + return !$$.isGaugeType(d.data) && withTransform ? "scale(0)" : ""; + }).on('mouseover', config.interaction_enabled ? function (d) { + var updated, arcData; + if ($$.transiting) { + // skip while transiting + return; + } + updated = $$.updateAngle(d); + if (updated) { + arcData = $$.convertToArcData(updated); + // transitions + $$.expandArc(updated.data.id); + $$.api.focus(updated.data.id); + $$.toggleFocusLegend(updated.data.id, true); + $$.config.data_onmouseover(arcData, this); + } + } : null).on('mousemove', config.interaction_enabled ? function (d) { + var updated = $$.updateAngle(d), + arcData, + selectedData; + if (updated) { + arcData = $$.convertToArcData(updated), selectedData = [arcData]; + $$.showTooltip(selectedData, this); + } + } : null).on('mouseout', config.interaction_enabled ? function (d) { + var updated, arcData; + if ($$.transiting) { + // skip while transiting + return; + } + updated = $$.updateAngle(d); + if (updated) { + arcData = $$.convertToArcData(updated); + // transitions + $$.unexpandArc(updated.data.id); + $$.api.revert(); + $$.revertLegend(); + $$.hideTooltip(); + $$.config.data_onmouseout(arcData, this); + } + } : null).on('click', config.interaction_enabled ? function (d, i) { + var updated = $$.updateAngle(d), + arcData; + if (updated) { + arcData = $$.convertToArcData(updated); + if ($$.toggleShape) { + $$.toggleShape(this, arcData, i); + } + $$.config.data_onclick.call($$.api, arcData, this); + } + } : null).each(function () { + $$.transiting = true; + }).transition().duration(duration).attrTween("d", function (d) { + var updated = $$.updateAngle(d), + interpolate; + if (!updated) { + return function () { + return "M 0 0"; + }; + } + // if (this._current === d) { + // this._current = { + // startAngle: Math.PI*2, + // endAngle: Math.PI*2, + // }; + // } + if (isNaN(this._current.startAngle)) { + this._current.startAngle = 0; + } + if (isNaN(this._current.endAngle)) { + this._current.endAngle = this._current.startAngle; + } + interpolate = d3.interpolate(this._current, updated); + this._current = interpolate(0); + return function (t) { + var interpolated = interpolate(t); + interpolated.data = d.data; // data.id will be updated by interporator + return $$.getArc(interpolated, true); + }; + }).attr("transform", withTransform ? "scale(1)" : "").style("fill", function (d) { + return $$.levelColor ? $$.levelColor(d.data.values[0].value) : $$.color(d.data.id); + } // Where gauge reading color would receive customization. + ).call($$.endall, function () { + $$.transiting = false; + }); + mainArc.exit().transition().duration(durationForExit).style('opacity', 0).remove(); + main.selectAll('.' + CLASS.chartArc).select('text').style("opacity", 0).attr('class', function (d) { + return $$.isGaugeType(d.data) ? CLASS.gaugeValue : ''; + }).text($$.textForArcLabel.bind($$)).attr("transform", $$.transformForArcLabel.bind($$)).style('font-size', function (d) { + return $$.isGaugeType(d.data) ? Math.round($$.radius / 5) + 'px' : ''; + }).transition().duration(duration).style("opacity", function (d) { + return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0; + }); + main.select('.' + CLASS.chartArcsTitle).style("opacity", $$.hasType('donut') || $$.hasType('gauge') ? 1 : 0); + + if ($$.hasType('gauge')) { + $$.arcs.select('.' + CLASS.chartArcsBackground).attr("d", function () { + var d = { + data: [{ value: config.gauge_max }], + startAngle: config.gauge_startingAngle, + endAngle: -1 * config.gauge_startingAngle + }; + return $$.getArc(d, true, true); + }); + $$.arcs.select('.' + CLASS.chartArcsGaugeUnit).attr("dy", ".75em").text(config.gauge_label_show ? config.gauge_units : ''); + $$.arcs.select('.' + CLASS.chartArcsGaugeMin).attr("dx", -1 * ($$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + "px").attr("dy", "1.2em").text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_min, false) : ''); + $$.arcs.select('.' + CLASS.chartArcsGaugeMax).attr("dx", $$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2) + "px").attr("dy", "1.2em").text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_max, true) : ''); + } +}; +c3_chart_internal_fn.initGauge = function () { + var arcs = this.arcs; + if (this.hasType('gauge')) { + arcs.append('path').attr("class", CLASS.chartArcsBackground); + arcs.append("text").attr("class", CLASS.chartArcsGaugeUnit).style("text-anchor", "middle").style("pointer-events", "none"); + arcs.append("text").attr("class", CLASS.chartArcsGaugeMin).style("text-anchor", "middle").style("pointer-events", "none"); + arcs.append("text").attr("class", CLASS.chartArcsGaugeMax).style("text-anchor", "middle").style("pointer-events", "none"); + } +}; +c3_chart_internal_fn.getGaugeLabelHeight = function () { + return this.config.gauge_label_show ? 20 : 0; +}; + +c3_chart_internal_fn.hasCaches = function (ids) { + for (var i = 0; i < ids.length; i++) { + if (!(ids[i] in this.cache)) { + return false; + } + } + return true; +}; +c3_chart_internal_fn.addCache = function (id, target) { + this.cache[id] = this.cloneTarget(target); +}; +c3_chart_internal_fn.getCaches = function (ids) { + var targets = [], + i; + for (i = 0; i < ids.length; i++) { + if (ids[i] in this.cache) { + targets.push(this.cloneTarget(this.cache[ids[i]])); + } + } + return targets; +}; + +c3_chart_internal_fn.categoryName = function (i) { + var config = this.config; + return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i; +}; + +c3_chart_internal_fn.generateClass = function (prefix, targetId) { + return " " + prefix + " " + prefix + this.getTargetSelectorSuffix(targetId); +}; +c3_chart_internal_fn.classText = function (d) { + return this.generateClass(CLASS.text, d.index); +}; +c3_chart_internal_fn.classTexts = function (d) { + return this.generateClass(CLASS.texts, d.id); +}; +c3_chart_internal_fn.classShape = function (d) { + return this.generateClass(CLASS.shape, d.index); +}; +c3_chart_internal_fn.classShapes = function (d) { + return this.generateClass(CLASS.shapes, d.id); +}; +c3_chart_internal_fn.classLine = function (d) { + return this.classShape(d) + this.generateClass(CLASS.line, d.id); +}; +c3_chart_internal_fn.classLines = function (d) { + return this.classShapes(d) + this.generateClass(CLASS.lines, d.id); +}; +c3_chart_internal_fn.classCircle = function (d) { + return this.classShape(d) + this.generateClass(CLASS.circle, d.index); +}; +c3_chart_internal_fn.classCircles = function (d) { + return this.classShapes(d) + this.generateClass(CLASS.circles, d.id); +}; +c3_chart_internal_fn.classBar = function (d) { + return this.classShape(d) + this.generateClass(CLASS.bar, d.index); +}; +c3_chart_internal_fn.classBars = function (d) { + return this.classShapes(d) + this.generateClass(CLASS.bars, d.id); +}; +c3_chart_internal_fn.classArc = function (d) { + return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id); +}; +c3_chart_internal_fn.classArcs = function (d) { + return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id); +}; +c3_chart_internal_fn.classArea = function (d) { + return this.classShape(d) + this.generateClass(CLASS.area, d.id); +}; +c3_chart_internal_fn.classAreas = function (d) { + return this.classShapes(d) + this.generateClass(CLASS.areas, d.id); +}; +c3_chart_internal_fn.classRegion = function (d, i) { + return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : ''); +}; +c3_chart_internal_fn.classEvent = function (d) { + return this.generateClass(CLASS.eventRect, d.index); +}; +c3_chart_internal_fn.classTarget = function (id) { + var $$ = this; + var additionalClassSuffix = $$.config.data_classes[id], + additionalClass = ''; + if (additionalClassSuffix) { + additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix; + } + return $$.generateClass(CLASS.target, id) + additionalClass; +}; +c3_chart_internal_fn.classFocus = function (d) { + return this.classFocused(d) + this.classDefocused(d); +}; +c3_chart_internal_fn.classFocused = function (d) { + return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : ''); +}; +c3_chart_internal_fn.classDefocused = function (d) { + return ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : ''); +}; +c3_chart_internal_fn.classChartText = function (d) { + return CLASS.chartText + this.classTarget(d.id); +}; +c3_chart_internal_fn.classChartLine = function (d) { + return CLASS.chartLine + this.classTarget(d.id); +}; +c3_chart_internal_fn.classChartBar = function (d) { + return CLASS.chartBar + this.classTarget(d.id); +}; +c3_chart_internal_fn.classChartArc = function (d) { + return CLASS.chartArc + this.classTarget(d.data.id); +}; +c3_chart_internal_fn.getTargetSelectorSuffix = function (targetId) { + return targetId || targetId === 0 ? ('-' + targetId).replace(/[\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g, '-') : ''; +}; +c3_chart_internal_fn.selectorTarget = function (id, prefix) { + return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id); +}; +c3_chart_internal_fn.selectorTargets = function (ids, prefix) { + var $$ = this; + ids = ids || []; + return ids.length ? ids.map(function (id) { + return $$.selectorTarget(id, prefix); + }) : null; +}; +c3_chart_internal_fn.selectorLegend = function (id) { + return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id); +}; +c3_chart_internal_fn.selectorLegends = function (ids) { + var $$ = this; + return ids && ids.length ? ids.map(function (id) { + return $$.selectorLegend(id); + }) : null; +}; + +c3_chart_internal_fn.getClipPath = function (id) { + var isIE9 = window.navigator.appVersion.toLowerCase().indexOf("msie 9.") >= 0; + return "url(" + (isIE9 ? "" : document.URL.split('#')[0]) + "#" + id + ")"; +}; +c3_chart_internal_fn.appendClip = function (parent, id) { + return parent.append("clipPath").attr("id", id).append("rect"); +}; +c3_chart_internal_fn.getAxisClipX = function (forHorizontal) { + // axis line width + padding for left + var left = Math.max(30, this.margin.left); + return forHorizontal ? -(1 + left) : -(left - 1); +}; +c3_chart_internal_fn.getAxisClipY = function (forHorizontal) { + return forHorizontal ? -20 : -this.margin.top; +}; +c3_chart_internal_fn.getXAxisClipX = function () { + var $$ = this; + return $$.getAxisClipX(!$$.config.axis_rotated); +}; +c3_chart_internal_fn.getXAxisClipY = function () { + var $$ = this; + return $$.getAxisClipY(!$$.config.axis_rotated); +}; +c3_chart_internal_fn.getYAxisClipX = function () { + var $$ = this; + return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated); +}; +c3_chart_internal_fn.getYAxisClipY = function () { + var $$ = this; + return $$.getAxisClipY($$.config.axis_rotated); +}; +c3_chart_internal_fn.getAxisClipWidth = function (forHorizontal) { + var $$ = this, + left = Math.max(30, $$.margin.left), + right = Math.max(30, $$.margin.right); + // width + axis line width + padding for left/right + return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20; +}; +c3_chart_internal_fn.getAxisClipHeight = function (forHorizontal) { + // less than 20 is not enough to show the axis label 'outer' without legend + return (forHorizontal ? this.margin.bottom : this.margin.top + this.height) + 20; +}; +c3_chart_internal_fn.getXAxisClipWidth = function () { + var $$ = this; + return $$.getAxisClipWidth(!$$.config.axis_rotated); +}; +c3_chart_internal_fn.getXAxisClipHeight = function () { + var $$ = this; + return $$.getAxisClipHeight(!$$.config.axis_rotated); +}; +c3_chart_internal_fn.getYAxisClipWidth = function () { + var $$ = this; + return $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0); +}; +c3_chart_internal_fn.getYAxisClipHeight = function () { + var $$ = this; + return $$.getAxisClipHeight($$.config.axis_rotated); +}; + +c3_chart_internal_fn.generateColor = function () { + var $$ = this, + config = $$.config, + d3 = $$.d3, + colors = config.data_colors, + pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.scale.category10().range(), + callback = config.data_color, + ids = []; + + return function (d) { + var id = d.id || d.data && d.data.id || d, + color; + + // if callback function is provided + if (colors[id] instanceof Function) { + color = colors[id](d); + } + // if specified, choose that color + else if (colors[id]) { + color = colors[id]; + } + // if not specified, choose from pattern + else { + if (ids.indexOf(id) < 0) { + ids.push(id); + } + color = pattern[ids.indexOf(id) % pattern.length]; + colors[id] = color; + } + return callback instanceof Function ? callback(color, d) : color; + }; +}; +c3_chart_internal_fn.generateLevelColor = function () { + var $$ = this, + config = $$.config, + colors = config.color_pattern, + threshold = config.color_threshold, + asValue = threshold.unit === 'value', + values = threshold.values && threshold.values.length ? threshold.values : [], + max = threshold.max || 100; + return notEmpty(config.color_threshold) ? function (value) { + var i, + v, + color = colors[colors.length - 1]; + for (i = 0; i < values.length; i++) { + v = asValue ? value : value * 100 / max; + if (v < values[i]) { + color = colors[i]; + break; + } + } + return color; + } : null; +}; + +c3_chart_internal_fn.getDefaultConfig = function () { + var config = { + bindto: '#chart', + svg_classname: undefined, + size_width: undefined, + size_height: undefined, + padding_left: undefined, + padding_right: undefined, + padding_top: undefined, + padding_bottom: undefined, + resize_auto: true, + zoom_enabled: false, + zoom_extent: undefined, + zoom_privileged: false, + zoom_rescale: false, + zoom_onzoom: function zoom_onzoom() {}, + zoom_onzoomstart: function zoom_onzoomstart() {}, + zoom_onzoomend: function zoom_onzoomend() {}, + zoom_x_min: undefined, + zoom_x_max: undefined, + interaction_brighten: true, + interaction_enabled: true, + onmouseover: function onmouseover() {}, + onmouseout: function onmouseout() {}, + onresize: function onresize() {}, + onresized: function onresized() {}, + oninit: function oninit() {}, + onrendered: function onrendered() {}, + transition_duration: 350, + data_x: undefined, + data_xs: {}, + data_xFormat: '%Y-%m-%d', + data_xLocaltime: true, + data_xSort: true, + data_idConverter: function data_idConverter(id) { + return id; + }, + data_names: {}, + data_classes: {}, + data_groups: [], + data_axes: {}, + data_type: undefined, + data_types: {}, + data_labels: {}, + data_order: 'desc', + data_regions: {}, + data_color: undefined, + data_colors: {}, + data_hide: false, + data_filter: undefined, + data_selection_enabled: false, + data_selection_grouped: false, + data_selection_isselectable: function data_selection_isselectable() { + return true; + }, + data_selection_multiple: true, + data_selection_draggable: false, + data_onclick: function data_onclick() {}, + data_onmouseover: function data_onmouseover() {}, + data_onmouseout: function data_onmouseout() {}, + data_onselected: function data_onselected() {}, + data_onunselected: function data_onunselected() {}, + data_url: undefined, + data_headers: undefined, + data_json: undefined, + data_rows: undefined, + data_columns: undefined, + data_mimeType: undefined, + data_keys: undefined, + // configuration for no plot-able data supplied. + data_empty_label_text: "", + // subchart + subchart_show: false, + subchart_size_height: 60, + subchart_axis_x_show: true, + subchart_onbrush: function subchart_onbrush() {}, + // color + color_pattern: [], + color_threshold: {}, + // legend + legend_show: true, + legend_hide: false, + legend_position: 'bottom', + legend_inset_anchor: 'top-left', + legend_inset_x: 10, + legend_inset_y: 0, + legend_inset_step: undefined, + legend_item_onclick: undefined, + legend_item_onmouseover: undefined, + legend_item_onmouseout: undefined, + legend_equally: false, + legend_padding: 0, + legend_item_tile_width: 10, + legend_item_tile_height: 10, + // axis + axis_rotated: false, + axis_x_show: true, + axis_x_type: 'indexed', + axis_x_localtime: true, + axis_x_categories: [], + axis_x_tick_centered: false, + axis_x_tick_format: undefined, + axis_x_tick_culling: {}, + axis_x_tick_culling_max: 10, + axis_x_tick_count: undefined, + axis_x_tick_fit: true, + axis_x_tick_values: null, + axis_x_tick_rotate: 0, + axis_x_tick_outer: true, + axis_x_tick_multiline: true, + axis_x_tick_width: null, + axis_x_max: undefined, + axis_x_min: undefined, + axis_x_padding: {}, + axis_x_height: undefined, + axis_x_extent: undefined, + axis_x_label: {}, + axis_y_show: true, + axis_y_type: undefined, + axis_y_max: undefined, + axis_y_min: undefined, + axis_y_inverted: false, + axis_y_center: undefined, + axis_y_inner: undefined, + axis_y_label: {}, + axis_y_tick_format: undefined, + axis_y_tick_outer: true, + axis_y_tick_values: null, + axis_y_tick_rotate: 0, + axis_y_tick_count: undefined, + axis_y_tick_time_value: undefined, + axis_y_tick_time_interval: undefined, + axis_y_padding: {}, + axis_y_default: undefined, + axis_y2_show: false, + axis_y2_max: undefined, + axis_y2_min: undefined, + axis_y2_inverted: false, + axis_y2_center: undefined, + axis_y2_inner: undefined, + axis_y2_label: {}, + axis_y2_tick_format: undefined, + axis_y2_tick_outer: true, + axis_y2_tick_values: null, + axis_y2_tick_count: undefined, + axis_y2_padding: {}, + axis_y2_default: undefined, + // grid + grid_x_show: false, + grid_x_type: 'tick', + grid_x_lines: [], + grid_y_show: false, + // not used + // grid_y_type: 'tick', + grid_y_lines: [], + grid_y_ticks: 10, + grid_focus_show: true, + grid_lines_front: true, + // point - point of each data + point_show: true, + point_r: 2.5, + point_sensitivity: 10, + point_focus_expand_enabled: true, + point_focus_expand_r: undefined, + point_select_r: undefined, + // line + line_connectNull: false, + line_step_type: 'step', + // bar + bar_width: undefined, + bar_width_ratio: 0.6, + bar_width_max: undefined, + bar_zerobased: true, + bar_space: 0, + // area + area_zerobased: true, + area_above: false, + // pie + pie_label_show: true, + pie_label_format: undefined, + pie_label_threshold: 0.05, + pie_label_ratio: undefined, + pie_expand: {}, + pie_expand_duration: 50, + // gauge + gauge_fullCircle: false, + gauge_label_show: true, + gauge_label_format: undefined, + gauge_min: 0, + gauge_max: 100, + gauge_startingAngle: -1 * Math.PI / 2, + gauge_label_extents: undefined, + gauge_units: undefined, + gauge_width: undefined, + gauge_expand: {}, + gauge_expand_duration: 50, + // donut + donut_label_show: true, + donut_label_format: undefined, + donut_label_threshold: 0.05, + donut_label_ratio: undefined, + donut_width: undefined, + donut_title: "", + donut_expand: {}, + donut_expand_duration: 50, + // spline + spline_interpolation_type: 'cardinal', + // region - region to change style + regions: [], + // tooltip - show when mouseover on each data + tooltip_show: true, + tooltip_grouped: true, + tooltip_order: undefined, + tooltip_format_title: undefined, + tooltip_format_name: undefined, + tooltip_format_value: undefined, + tooltip_position: undefined, + tooltip_contents: function tooltip_contents(d, defaultTitleFormat, defaultValueFormat, color) { + return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : ''; + }, + tooltip_init_show: false, + tooltip_init_x: 0, + tooltip_init_position: { top: '0px', left: '50px' }, + tooltip_onshow: function tooltip_onshow() {}, + tooltip_onhide: function tooltip_onhide() {}, + // title + title_text: undefined, + title_padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + }, + title_position: 'top-center' + }; + + Object.keys(this.additionalConfig).forEach(function (key) { + config[key] = this.additionalConfig[key]; + }, this); + + return config; +}; +c3_chart_internal_fn.additionalConfig = {}; + +c3_chart_internal_fn.loadConfig = function (config) { + var this_config = this.config, + target, + keys, + read; + function find() { + var key = keys.shift(); + // console.log("key =>", key, ", target =>", target); + if (key && target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && key in target) { + target = target[key]; + return find(); + } else if (!key) { + return target; + } else { + return undefined; + } + } + Object.keys(this_config).forEach(function (key) { + target = config; + keys = key.split('_'); + read = find(); + // console.log("CONFIG : ", key, read); + if (isDefined(read)) { + this_config[key] = read; + } + }); +}; + +c3_chart_internal_fn.convertUrlToData = function (url, mimeType, headers, keys, done) { + var $$ = this, + type = mimeType ? mimeType : 'csv'; + var req = $$.d3.xhr(url); + if (headers) { + Object.keys(headers).forEach(function (header) { + req.header(header, headers[header]); + }); + } + req.get(function (error, data) { + var d; + var dataResponse = data.response || data.responseText; // Fixes IE9 XHR issue; see #1345 + if (!data) { + throw new Error(error.responseURL + ' ' + error.status + ' (' + error.statusText + ')'); + } + if (type === 'json') { + d = $$.convertJsonToData(JSON.parse(dataResponse), keys); + } else if (type === 'tsv') { + d = $$.convertTsvToData(dataResponse); + } else { + d = $$.convertCsvToData(dataResponse); + } + done.call($$, d); + }); +}; +c3_chart_internal_fn.convertXsvToData = function (xsv, parser) { + var rows = parser.parseRows(xsv), + d; + if (rows.length === 1) { + d = [{}]; + rows[0].forEach(function (id) { + d[0][id] = null; + }); + } else { + d = parser.parse(xsv); + } + return d; +}; +c3_chart_internal_fn.convertCsvToData = function (csv) { + return this.convertXsvToData(csv, this.d3.csv); +}; +c3_chart_internal_fn.convertTsvToData = function (tsv) { + return this.convertXsvToData(tsv, this.d3.tsv); +}; +c3_chart_internal_fn.convertJsonToData = function (json, keys) { + var $$ = this, + new_rows = [], + targetKeys, + data; + if (keys) { + // when keys specified, json would be an array that includes objects + if (keys.x) { + targetKeys = keys.value.concat(keys.x); + $$.config.data_x = keys.x; + } else { + targetKeys = keys.value; + } + new_rows.push(targetKeys); + json.forEach(function (o) { + var new_row = []; + targetKeys.forEach(function (key) { + // convert undefined to null because undefined data will be removed in convertDataToTargets() + var v = $$.findValueInJson(o, key); + if (isUndefined(v)) { + v = null; + } + new_row.push(v); + }); + new_rows.push(new_row); + }); + data = $$.convertRowsToData(new_rows); + } else { + Object.keys(json).forEach(function (key) { + new_rows.push([key].concat(json[key])); + }); + data = $$.convertColumnsToData(new_rows); + } + return data; +}; +c3_chart_internal_fn.findValueInJson = function (object, path) { + path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties (replace [] with .) + path = path.replace(/^\./, ''); // strip a leading dot + var pathArray = path.split('.'); + for (var i = 0; i < pathArray.length; ++i) { + var k = pathArray[i]; + if (k in object) { + object = object[k]; + } else { + return; + } + } + return object; +}; + +/** + * Converts the rows to normalized data. + * @param {any[][]} rows The row data + * @return {Object[]} + */ +c3_chart_internal_fn.convertRowsToData = function (rows) { + var newRows = []; + var keys = rows[0]; + + for (var i = 1; i < rows.length; i++) { + var newRow = {}; + for (var j = 0; j < rows[i].length; j++) { + if (isUndefined(rows[i][j])) { + throw new Error("Source data is missing a component at (" + i + "," + j + ")!"); + } + newRow[keys[j]] = rows[i][j]; + } + newRows.push(newRow); + } + return newRows; +}; + +/** + * Converts the columns to normalized data. + * @param {any[][]} columns The column data + * @return {Object[]} + */ +c3_chart_internal_fn.convertColumnsToData = function (columns) { + var newRows = []; + + for (var i = 0; i < columns.length; i++) { + var key = columns[i][0]; + for (var j = 1; j < columns[i].length; j++) { + if (isUndefined(newRows[j - 1])) { + newRows[j - 1] = {}; + } + if (isUndefined(columns[i][j])) { + throw new Error("Source data is missing a component at (" + i + "," + j + ")!"); + } + newRows[j - 1][key] = columns[i][j]; + } + } + + return newRows; +}; + +c3_chart_internal_fn.convertDataToTargets = function (data, appendXs) { + var $$ = this, + config = $$.config, + ids = $$.d3.keys(data[0]).filter($$.isNotX, $$), + xs = $$.d3.keys(data[0]).filter($$.isX, $$), + targets; + + // save x for update data by load when custom x and c3.x API + ids.forEach(function (id) { + var xKey = $$.getXKey(id); + + if ($$.isCustomX() || $$.isTimeSeries()) { + // if included in input data + if (xs.indexOf(xKey) >= 0) { + $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : []).concat(data.map(function (d) { + return d[xKey]; + }).filter(isValue).map(function (rawX, i) { + return $$.generateTargetX(rawX, id, i); + })); + } + // if not included in input data, find from preloaded data of other id's x + else if (config.data_x) { + $$.data.xs[id] = $$.getOtherTargetXs(); + } + // if not included in input data, find from preloaded data + else if (notEmpty(config.data_xs)) { + $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets); + } + // MEMO: if no x included, use same x of current will be used + } else { + $$.data.xs[id] = data.map(function (d, i) { + return i; + }); + } + }); + + // check x is defined + ids.forEach(function (id) { + if (!$$.data.xs[id]) { + throw new Error('x is not defined for id = "' + id + '".'); + } + }); + + // convert to target + targets = ids.map(function (id, index) { + var convertedId = config.data_idConverter(id); + return { + id: convertedId, + id_org: id, + values: data.map(function (d, i) { + var xKey = $$.getXKey(id), + rawX = d[xKey], + value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null, + x; + // use x as categories if custom x and categorized + if ($$.isCustomX() && $$.isCategorized() && !isUndefined(rawX)) { + if (index === 0 && i === 0) { + config.axis_x_categories = []; + } + x = config.axis_x_categories.indexOf(rawX); + if (x === -1) { + x = config.axis_x_categories.length; + config.axis_x_categories.push(rawX); + } + } else { + x = $$.generateTargetX(rawX, id, i); + } + // mark as x = undefined if value is undefined and filter to remove after mapped + if (isUndefined(d[id]) || $$.data.xs[id].length <= i) { + x = undefined; + } + return { x: x, value: value, id: convertedId }; + }).filter(function (v) { + return isDefined(v.x); + }) + }; + }); + + // finish targets + targets.forEach(function (t) { + var i; + // sort values by its x + if (config.data_xSort) { + t.values = t.values.sort(function (v1, v2) { + var x1 = v1.x || v1.x === 0 ? v1.x : Infinity, + x2 = v2.x || v2.x === 0 ? v2.x : Infinity; + return x1 - x2; + }); + } + // indexing each value + i = 0; + t.values.forEach(function (v) { + v.index = i++; + }); + // this needs to be sorted because its index and value.index is identical + $$.data.xs[t.id].sort(function (v1, v2) { + return v1 - v2; + }); + }); + + // cache information about values + $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets); + $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets); + + // set target types + if (config.data_type) { + $$.setTargetType($$.mapToIds(targets).filter(function (id) { + return !(id in config.data_types); + }), config.data_type); + } + + // cache as original id keyed + targets.forEach(function (d) { + $$.addCache(d.id_org, d); + }); + + return targets; +}; + +c3_chart_internal_fn.isX = function (key) { + var $$ = this, + config = $$.config; + return config.data_x && key === config.data_x || notEmpty(config.data_xs) && hasValue(config.data_xs, key); +}; +c3_chart_internal_fn.isNotX = function (key) { + return !this.isX(key); +}; +c3_chart_internal_fn.getXKey = function (id) { + var $$ = this, + config = $$.config; + return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null; +}; +c3_chart_internal_fn.getXValuesOfXKey = function (key, targets) { + var $$ = this, + xValues, + ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : []; + ids.forEach(function (id) { + if ($$.getXKey(id) === key) { + xValues = $$.data.xs[id]; + } + }); + return xValues; +}; +c3_chart_internal_fn.getIndexByX = function (x) { + var $$ = this, + data = $$.filterByX($$.data.targets, x); + return data.length ? data[0].index : null; +}; +c3_chart_internal_fn.getXValue = function (id, i) { + var $$ = this; + return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i; +}; +c3_chart_internal_fn.getOtherTargetXs = function () { + var $$ = this, + idsForX = Object.keys($$.data.xs); + return idsForX.length ? $$.data.xs[idsForX[0]] : null; +}; +c3_chart_internal_fn.getOtherTargetX = function (index) { + var xs = this.getOtherTargetXs(); + return xs && index < xs.length ? xs[index] : null; +}; +c3_chart_internal_fn.addXs = function (xs) { + var $$ = this; + Object.keys(xs).forEach(function (id) { + $$.config.data_xs[id] = xs[id]; + }); +}; +c3_chart_internal_fn.hasMultipleX = function (xs) { + return this.d3.set(Object.keys(xs).map(function (id) { + return xs[id]; + })).size() > 1; +}; +c3_chart_internal_fn.isMultipleX = function () { + return notEmpty(this.config.data_xs) || !this.config.data_xSort || this.hasType('scatter'); +}; +c3_chart_internal_fn.addName = function (data) { + var $$ = this, + name; + if (data) { + name = $$.config.data_names[data.id]; + data.name = name !== undefined ? name : data.id; + } + return data; +}; +c3_chart_internal_fn.getValueOnIndex = function (values, index) { + var valueOnIndex = values.filter(function (v) { + return v.index === index; + }); + return valueOnIndex.length ? valueOnIndex[0] : null; +}; +c3_chart_internal_fn.updateTargetX = function (targets, x) { + var $$ = this; + targets.forEach(function (t) { + t.values.forEach(function (v, i) { + v.x = $$.generateTargetX(x[i], t.id, i); + }); + $$.data.xs[t.id] = x; + }); +}; +c3_chart_internal_fn.updateTargetXs = function (targets, xs) { + var $$ = this; + targets.forEach(function (t) { + if (xs[t.id]) { + $$.updateTargetX([t], xs[t.id]); + } + }); +}; +c3_chart_internal_fn.generateTargetX = function (rawX, id, index) { + var $$ = this, + x; + if ($$.isTimeSeries()) { + x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index)); + } else if ($$.isCustomX() && !$$.isCategorized()) { + x = isValue(rawX) ? +rawX : $$.getXValue(id, index); + } else { + x = index; + } + return x; +}; +c3_chart_internal_fn.cloneTarget = function (target) { + return { + id: target.id, + id_org: target.id_org, + values: target.values.map(function (d) { + return { x: d.x, value: d.value, id: d.id }; + }) + }; +}; +c3_chart_internal_fn.updateXs = function () { + var $$ = this; + if ($$.data.targets.length) { + $$.xs = []; + $$.data.targets[0].values.forEach(function (v) { + $$.xs[v.index] = v.x; + }); + } +}; +c3_chart_internal_fn.getPrevX = function (i) { + var x = this.xs[i - 1]; + return typeof x !== 'undefined' ? x : null; +}; +c3_chart_internal_fn.getNextX = function (i) { + var x = this.xs[i + 1]; + return typeof x !== 'undefined' ? x : null; +}; +c3_chart_internal_fn.getMaxDataCount = function () { + var $$ = this; + return $$.d3.max($$.data.targets, function (t) { + return t.values.length; + }); +}; +c3_chart_internal_fn.getMaxDataCountTarget = function (targets) { + var length = targets.length, + max = 0, + maxTarget; + if (length > 1) { + targets.forEach(function (t) { + if (t.values.length > max) { + maxTarget = t; + max = t.values.length; + } + }); + } else { + maxTarget = length ? targets[0] : null; + } + return maxTarget; +}; +c3_chart_internal_fn.getEdgeX = function (targets) { + var $$ = this; + return !targets.length ? [0, 0] : [$$.d3.min(targets, function (t) { + return t.values[0].x; + }), $$.d3.max(targets, function (t) { + return t.values[t.values.length - 1].x; + })]; +}; +c3_chart_internal_fn.mapToIds = function (targets) { + return targets.map(function (d) { + return d.id; + }); +}; +c3_chart_internal_fn.mapToTargetIds = function (ids) { + var $$ = this; + return ids ? [].concat(ids) : $$.mapToIds($$.data.targets); +}; +c3_chart_internal_fn.hasTarget = function (targets, id) { + var ids = this.mapToIds(targets), + i; + for (i = 0; i < ids.length; i++) { + if (ids[i] === id) { + return true; + } + } + return false; +}; +c3_chart_internal_fn.isTargetToShow = function (targetId) { + return this.hiddenTargetIds.indexOf(targetId) < 0; +}; +c3_chart_internal_fn.isLegendToShow = function (targetId) { + return this.hiddenLegendIds.indexOf(targetId) < 0; +}; +c3_chart_internal_fn.filterTargetsToShow = function (targets) { + var $$ = this; + return targets.filter(function (t) { + return $$.isTargetToShow(t.id); + }); +}; +c3_chart_internal_fn.mapTargetsToUniqueXs = function (targets) { + var $$ = this; + var xs = $$.d3.set($$.d3.merge(targets.map(function (t) { + return t.values.map(function (v) { + return +v.x; + }); + }))).values(); + xs = $$.isTimeSeries() ? xs.map(function (x) { + return new Date(+x); + }) : xs.map(function (x) { + return +x; + }); + return xs.sort(function (a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + }); +}; +c3_chart_internal_fn.addHiddenTargetIds = function (targetIds) { + targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); + for (var i = 0; i < targetIds.length; i++) { + if (this.hiddenTargetIds.indexOf(targetIds[i]) < 0) { + this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds[i]); + } + } +}; +c3_chart_internal_fn.removeHiddenTargetIds = function (targetIds) { + this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) { + return targetIds.indexOf(id) < 0; + }); +}; +c3_chart_internal_fn.addHiddenLegendIds = function (targetIds) { + targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); + for (var i = 0; i < targetIds.length; i++) { + if (this.hiddenLegendIds.indexOf(targetIds[i]) < 0) { + this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds[i]); + } + } +}; +c3_chart_internal_fn.removeHiddenLegendIds = function (targetIds) { + this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) { + return targetIds.indexOf(id) < 0; + }); +}; +c3_chart_internal_fn.getValuesAsIdKeyed = function (targets) { + var ys = {}; + targets.forEach(function (t) { + ys[t.id] = []; + t.values.forEach(function (v) { + ys[t.id].push(v.value); + }); + }); + return ys; +}; +c3_chart_internal_fn.checkValueInTargets = function (targets, checker) { + var ids = Object.keys(targets), + i, + j, + values; + for (i = 0; i < ids.length; i++) { + values = targets[ids[i]].values; + for (j = 0; j < values.length; j++) { + if (checker(values[j].value)) { + return true; + } + } + } + return false; +}; +c3_chart_internal_fn.hasNegativeValueInTargets = function (targets) { + return this.checkValueInTargets(targets, function (v) { + return v < 0; + }); +}; +c3_chart_internal_fn.hasPositiveValueInTargets = function (targets) { + return this.checkValueInTargets(targets, function (v) { + return v > 0; + }); +}; +c3_chart_internal_fn.isOrderDesc = function () { + var config = this.config; + return typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc'; +}; +c3_chart_internal_fn.isOrderAsc = function () { + var config = this.config; + return typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc'; +}; +c3_chart_internal_fn.getOrderFunction = function () { + var $$ = this, + config = $$.config, + orderAsc = $$.isOrderAsc(), + orderDesc = $$.isOrderDesc(); + if (orderAsc || orderDesc) { + return function (t1, t2) { + var reducer = function reducer(p, c) { + return p + Math.abs(c.value); + }; + var t1Sum = t1.values.reduce(reducer, 0), + t2Sum = t2.values.reduce(reducer, 0); + return orderDesc ? t2Sum - t1Sum : t1Sum - t2Sum; + }; + } else if (isFunction(config.data_order)) { + return config.data_order; + } else if (isArray(config.data_order)) { + var order = config.data_order; + return function (t1, t2) { + return order.indexOf(t1.id) - order.indexOf(t2.id); + }; + } +}; +c3_chart_internal_fn.orderTargets = function (targets) { + var fct = this.getOrderFunction(); + if (fct) { + targets.sort(fct); + if (this.isOrderAsc() || this.isOrderDesc()) { + targets.reverse(); + } + } + return targets; +}; +c3_chart_internal_fn.filterByX = function (targets, x) { + return this.d3.merge(targets.map(function (t) { + return t.values; + })).filter(function (v) { + return v.x - x === 0; + }); +}; +c3_chart_internal_fn.filterRemoveNull = function (data) { + return data.filter(function (d) { + return isValue(d.value); + }); +}; +c3_chart_internal_fn.filterByXDomain = function (targets, xDomain) { + return targets.map(function (t) { + return { + id: t.id, + id_org: t.id_org, + values: t.values.filter(function (v) { + return xDomain[0] <= v.x && v.x <= xDomain[1]; + }) + }; + }); +}; +c3_chart_internal_fn.hasDataLabel = function () { + var config = this.config; + if (typeof config.data_labels === 'boolean' && config.data_labels) { + return true; + } else if (_typeof(config.data_labels) === 'object' && notEmpty(config.data_labels)) { + return true; + } + return false; +}; +c3_chart_internal_fn.getDataLabelLength = function (min, max, key) { + var $$ = this, + lengths = [0, 0], + paddingCoef = 1.3; + $$.selectChart.select('svg').selectAll('.dummy').data([min, max]).enter().append('text').text(function (d) { + return $$.dataLabelFormat(d.id)(d); + }).each(function (d, i) { + lengths[i] = this.getBoundingClientRect()[key] * paddingCoef; + }).remove(); + return lengths; +}; +c3_chart_internal_fn.isNoneArc = function (d) { + return this.hasTarget(this.data.targets, d.id); +}, c3_chart_internal_fn.isArc = function (d) { + return 'data' in d && this.hasTarget(this.data.targets, d.data.id); +}; +c3_chart_internal_fn.findSameXOfValues = function (values, index) { + var i, + targetX = values[index].x, + sames = []; + for (i = index - 1; i >= 0; i--) { + if (targetX !== values[i].x) { + break; + } + sames.push(values[i]); + } + for (i = index; i < values.length; i++) { + if (targetX !== values[i].x) { + break; + } + sames.push(values[i]); + } + return sames; +}; + +c3_chart_internal_fn.findClosestFromTargets = function (targets, pos) { + var $$ = this, + candidates; + + // map to array of closest points of each target + candidates = targets.map(function (target) { + return $$.findClosest(target.values, pos); + }); + + // decide closest point and return + return $$.findClosest(candidates, pos); +}; +c3_chart_internal_fn.findClosest = function (values, pos) { + var $$ = this, + minDist = $$.config.point_sensitivity, + closest; + + // find mouseovering bar + values.filter(function (v) { + return v && $$.isBarType(v.id); + }).forEach(function (v) { + var shape = $$.main.select('.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index).node(); + if (!closest && $$.isWithinBar(shape)) { + closest = v; + } + }); + + // find closest point from non-bar + values.filter(function (v) { + return v && !$$.isBarType(v.id); + }).forEach(function (v) { + var d = $$.dist(v, pos); + if (d < minDist) { + minDist = d; + closest = v; + } + }); + + return closest; +}; +c3_chart_internal_fn.dist = function (data, pos) { + var $$ = this, + config = $$.config, + xIndex = config.axis_rotated ? 1 : 0, + yIndex = config.axis_rotated ? 0 : 1, + y = $$.circleY(data, data.index), + x = $$.x(data.x); + return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2)); +}; +c3_chart_internal_fn.convertValuesToStep = function (values) { + var converted = [].concat(values), + i; + + if (!this.isCategorized()) { + return values; + } + + for (i = values.length + 1; 0 < i; i--) { + converted[i] = converted[i - 1]; + } + + converted[0] = { + x: converted[0].x - 1, + value: converted[0].value, + id: converted[0].id + }; + converted[values.length + 1] = { + x: converted[values.length].x + 1, + value: converted[values.length].value, + id: converted[values.length].id + }; + + return converted; +}; +c3_chart_internal_fn.updateDataAttributes = function (name, attrs) { + var $$ = this, + config = $$.config, + current = config['data_' + name]; + if (typeof attrs === 'undefined') { + return current; + } + Object.keys(attrs).forEach(function (id) { + current[id] = attrs[id]; + }); + $$.redraw({ withLegend: true }); + return current; +}; + +c3_chart_internal_fn.load = function (targets, args) { + var $$ = this; + if (targets) { + // filter loading targets if needed + if (args.filter) { + targets = targets.filter(args.filter); + } + // set type if args.types || args.type specified + if (args.type || args.types) { + targets.forEach(function (t) { + var type = args.types && args.types[t.id] ? args.types[t.id] : args.type; + $$.setTargetType(t.id, type); + }); + } + // Update/Add data + $$.data.targets.forEach(function (d) { + for (var i = 0; i < targets.length; i++) { + if (d.id === targets[i].id) { + d.values = targets[i].values; + targets.splice(i, 1); + break; + } + } + }); + $$.data.targets = $$.data.targets.concat(targets); // add remained + } + + // Set targets + $$.updateTargets($$.data.targets); + + // Redraw with new targets + $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); + + if (args.done) { + args.done(); + } +}; +c3_chart_internal_fn.loadFromArgs = function (args) { + var $$ = this; + if (args.data) { + $$.load($$.convertDataToTargets(args.data), args); + } else if (args.url) { + $$.convertUrlToData(args.url, args.mimeType, args.headers, args.keys, function (data) { + $$.load($$.convertDataToTargets(data), args); + }); + } else if (args.json) { + $$.load($$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args); + } else if (args.rows) { + $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args); + } else if (args.columns) { + $$.load($$.convertDataToTargets($$.convertColumnsToData(args.columns)), args); + } else { + $$.load(null, args); + } +}; +c3_chart_internal_fn.unload = function (targetIds, done) { + var $$ = this; + if (!done) { + done = function done() {}; + } + // filter existing target + targetIds = targetIds.filter(function (id) { + return $$.hasTarget($$.data.targets, id); + }); + // If no target, call done and return + if (!targetIds || targetIds.length === 0) { + done(); + return; + } + $$.svg.selectAll(targetIds.map(function (id) { + return $$.selectorTarget(id); + })).transition().style('opacity', 0).remove().call($$.endall, done); + targetIds.forEach(function (id) { + // Reset fadein for future load + $$.withoutFadeIn[id] = false; + // Remove target's elements + if ($$.legend) { + $$.legend.selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)).remove(); + } + // Remove target + $$.data.targets = $$.data.targets.filter(function (t) { + return t.id !== id; + }); + }); +}; + +c3_chart_internal_fn.getYDomainMin = function (targets) { + var $$ = this, + config = $$.config, + ids = $$.mapToIds(targets), + ys = $$.getValuesAsIdKeyed(targets), + j, + k, + baseId, + idsInGroup, + id, + hasNegativeValue; + if (config.data_groups.length > 0) { + hasNegativeValue = $$.hasNegativeValueInTargets(targets); + for (j = 0; j < config.data_groups.length; j++) { + // Determine baseId + idsInGroup = config.data_groups[j].filter(function (id) { + return ids.indexOf(id) >= 0; + }); + if (idsInGroup.length === 0) { + continue; + } + baseId = idsInGroup[0]; + // Consider negative values + if (hasNegativeValue && ys[baseId]) { + ys[baseId].forEach(function (v, i) { + ys[baseId][i] = v < 0 ? v : 0; + }); + } + // Compute min + for (k = 1; k < idsInGroup.length; k++) { + id = idsInGroup[k]; + if (!ys[id]) { + continue; + } + ys[id].forEach(function (v, i) { + if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) { + ys[baseId][i] += +v; + } + }); + } + } + } + return $$.d3.min(Object.keys(ys).map(function (key) { + return $$.d3.min(ys[key]); + })); +}; +c3_chart_internal_fn.getYDomainMax = function (targets) { + var $$ = this, + config = $$.config, + ids = $$.mapToIds(targets), + ys = $$.getValuesAsIdKeyed(targets), + j, + k, + baseId, + idsInGroup, + id, + hasPositiveValue; + if (config.data_groups.length > 0) { + hasPositiveValue = $$.hasPositiveValueInTargets(targets); + for (j = 0; j < config.data_groups.length; j++) { + // Determine baseId + idsInGroup = config.data_groups[j].filter(function (id) { + return ids.indexOf(id) >= 0; + }); + if (idsInGroup.length === 0) { + continue; + } + baseId = idsInGroup[0]; + // Consider positive values + if (hasPositiveValue && ys[baseId]) { + ys[baseId].forEach(function (v, i) { + ys[baseId][i] = v > 0 ? v : 0; + }); + } + // Compute max + for (k = 1; k < idsInGroup.length; k++) { + id = idsInGroup[k]; + if (!ys[id]) { + continue; + } + ys[id].forEach(function (v, i) { + if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) { + ys[baseId][i] += +v; + } + }); + } + } + } + return $$.d3.max(Object.keys(ys).map(function (key) { + return $$.d3.max(ys[key]); + })); +}; +c3_chart_internal_fn.getYDomain = function (targets, axisId, xDomain) { + var $$ = this, + config = $$.config, + targetsByAxisId = targets.filter(function (t) { + return $$.axis.getId(t.id) === axisId; + }), + yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId, + yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min, + yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max, + yDomainMin = $$.getYDomainMin(yTargets), + yDomainMax = $$.getYDomainMax(yTargets), + domain, + domainLength, + padding, + padding_top, + padding_bottom, + center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center, + yDomainAbs, + lengths, + diff, + ratio, + isAllPositive, + isAllNegative, + isZeroBased = $$.hasType('bar', yTargets) && config.bar_zerobased || $$.hasType('area', yTargets) && config.area_zerobased, + isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted, + showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated, + showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated; + + // MEMO: avoid inverting domain unexpectedly + yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? yDomainMin < yMax ? yDomainMin : yMax - 10 : yDomainMin; + yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? yMin < yDomainMax ? yDomainMax : yMin + 10 : yDomainMax; + + if (yTargets.length === 0) { + // use current domain if target of axisId is none + return axisId === 'y2' ? $$.y2.domain() : $$.y.domain(); + } + if (isNaN(yDomainMin)) { + // set minimum to zero when not number + yDomainMin = 0; + } + if (isNaN(yDomainMax)) { + // set maximum to have same value as yDomainMin + yDomainMax = yDomainMin; + } + if (yDomainMin === yDomainMax) { + yDomainMin < 0 ? yDomainMax = 0 : yDomainMin = 0; + } + isAllPositive = yDomainMin >= 0 && yDomainMax >= 0; + isAllNegative = yDomainMin <= 0 && yDomainMax <= 0; + + // Cancel zerobased if axis_*_min / axis_*_max specified + if (isValue(yMin) && isAllPositive || isValue(yMax) && isAllNegative) { + isZeroBased = false; + } + + // Bar/Area chart should be 0-based if all positive|negative + if (isZeroBased) { + if (isAllPositive) { + yDomainMin = 0; + } + if (isAllNegative) { + yDomainMax = 0; + } + } + + domainLength = Math.abs(yDomainMax - yDomainMin); + padding = padding_top = padding_bottom = domainLength * 0.1; + + if (typeof center !== 'undefined') { + yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax)); + yDomainMax = center + yDomainAbs; + yDomainMin = center - yDomainAbs; + } + // add padding for data label + if (showHorizontalDataLabel) { + lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width'); + diff = diffDomain($$.y.range()); + ratio = [lengths[0] / diff, lengths[1] / diff]; + padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1])); + padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1])); + } else if (showVerticalDataLabel) { + lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height'); + padding_top += $$.axis.convertPixelsToAxisPadding(lengths[1], domainLength); + padding_bottom += $$.axis.convertPixelsToAxisPadding(lengths[0], domainLength); + } + if (axisId === 'y' && notEmpty(config.axis_y_padding)) { + padding_top = $$.axis.getPadding(config.axis_y_padding, 'top', padding_top, domainLength); + padding_bottom = $$.axis.getPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength); + } + if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) { + padding_top = $$.axis.getPadding(config.axis_y2_padding, 'top', padding_top, domainLength); + padding_bottom = $$.axis.getPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength); + } + // Bar/Area chart should be 0-based if all positive|negative + if (isZeroBased) { + if (isAllPositive) { + padding_bottom = yDomainMin; + } + if (isAllNegative) { + padding_top = -yDomainMax; + } + } + domain = [yDomainMin - padding_bottom, yDomainMax + padding_top]; + return isInverted ? domain.reverse() : domain; +}; +c3_chart_internal_fn.getXDomainMin = function (targets) { + var $$ = this, + config = $$.config; + return isDefined(config.axis_x_min) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min : $$.d3.min(targets, function (t) { + return $$.d3.min(t.values, function (v) { + return v.x; + }); + }); +}; +c3_chart_internal_fn.getXDomainMax = function (targets) { + var $$ = this, + config = $$.config; + return isDefined(config.axis_x_max) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max : $$.d3.max(targets, function (t) { + return $$.d3.max(t.values, function (v) { + return v.x; + }); + }); +}; +c3_chart_internal_fn.getXDomainPadding = function (domain) { + var $$ = this, + config = $$.config, + diff = domain[1] - domain[0], + maxDataCount, + padding, + paddingLeft, + paddingRight; + if ($$.isCategorized()) { + padding = 0; + } else if ($$.hasType('bar')) { + maxDataCount = $$.getMaxDataCount(); + padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5; + } else { + padding = diff * 0.01; + } + if (_typeof(config.axis_x_padding) === 'object' && notEmpty(config.axis_x_padding)) { + paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding; + paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding; + } else if (typeof config.axis_x_padding === 'number') { + paddingLeft = paddingRight = config.axis_x_padding; + } else { + paddingLeft = paddingRight = padding; + } + return { left: paddingLeft, right: paddingRight }; +}; +c3_chart_internal_fn.getXDomain = function (targets) { + var $$ = this, + xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)], + firstX = xDomain[0], + lastX = xDomain[1], + padding = $$.getXDomainPadding(xDomain), + min = 0, + max = 0; + // show center of x domain if min and max are the same + if (firstX - lastX === 0 && !$$.isCategorized()) { + if ($$.isTimeSeries()) { + firstX = new Date(firstX.getTime() * 0.5); + lastX = new Date(lastX.getTime() * 1.5); + } else { + firstX = firstX === 0 ? 1 : firstX * 0.5; + lastX = lastX === 0 ? -1 : lastX * 1.5; + } + } + if (firstX || firstX === 0) { + min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left; + } + if (lastX || lastX === 0) { + max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right; + } + return [min, max]; +}; +c3_chart_internal_fn.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain) { + var $$ = this, + config = $$.config; + + if (withUpdateOrgXDomain) { + $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets))); + $$.orgXDomain = $$.x.domain(); + if (config.zoom_enabled) { + $$.zoom.scale($$.x).updateScaleExtent(); + } + $$.subX.domain($$.x.domain()); + if ($$.brush) { + $$.brush.scale($$.subX); + } + } + if (withUpdateXDomain) { + $$.x.domain(domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.extent()); + if (config.zoom_enabled) { + $$.zoom.scale($$.x).updateScaleExtent(); + } + } + + // Trim domain when too big by zoom mousemove event + if (withTrim) { + $$.x.domain($$.trimXDomain($$.x.orgDomain())); + } + + return $$.x.domain(); +}; +c3_chart_internal_fn.trimXDomain = function (domain) { + var zoomDomain = this.getZoomDomain(), + min = zoomDomain[0], + max = zoomDomain[1]; + if (domain[0] <= min) { + domain[1] = +domain[1] + (min - domain[0]); + domain[0] = min; + } + if (max <= domain[1]) { + domain[0] = +domain[0] - (domain[1] - max); + domain[1] = max; + } + return domain; +}; + +c3_chart_internal_fn.drag = function (mouse) { + var $$ = this, + config = $$.config, + main = $$.main, + d3 = $$.d3; + var sx, sy, mx, my, minX, maxX, minY, maxY; + + if ($$.hasArcType()) { + return; + } + if (!config.data_selection_enabled) { + return; + } // do nothing if not selectable + if (config.zoom_enabled && !$$.zoom.altDomain) { + return; + } // skip if zoomable because of conflict drag dehavior + if (!config.data_selection_multiple) { + return; + } // skip when single selection because drag is used for multiple selection + + sx = $$.dragStart[0]; + sy = $$.dragStart[1]; + mx = mouse[0]; + my = mouse[1]; + minX = Math.min(sx, mx); + maxX = Math.max(sx, mx); + minY = config.data_selection_grouped ? $$.margin.top : Math.min(sy, my); + maxY = config.data_selection_grouped ? $$.height : Math.max(sy, my); + + main.select('.' + CLASS.dragarea).attr('x', minX).attr('y', minY).attr('width', maxX - minX).attr('height', maxY - minY); + // TODO: binary search when multiple xs + main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).filter(function (d) { + return config.data_selection_isselectable(d); + }).each(function (d, i) { + var shape = d3.select(this), + isSelected = shape.classed(CLASS.SELECTED), + isIncluded = shape.classed(CLASS.INCLUDED), + _x, + _y, + _w, + _h, + toggle, + isWithin = false, + box; + if (shape.classed(CLASS.circle)) { + _x = shape.attr("cx") * 1; + _y = shape.attr("cy") * 1; + toggle = $$.togglePoint; + isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY; + } else if (shape.classed(CLASS.bar)) { + box = getPathBox(this); + _x = box.x; + _y = box.y; + _w = box.width; + _h = box.height; + toggle = $$.togglePath; + isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY); + } else { + // line/area selection not supported yet + return; + } + if (isWithin ^ isIncluded) { + shape.classed(CLASS.INCLUDED, !isIncluded); + // TODO: included/unincluded callback here + shape.classed(CLASS.SELECTED, !isSelected); + toggle.call($$, !isSelected, shape, d, i); + } + }); +}; + +c3_chart_internal_fn.dragstart = function (mouse) { + var $$ = this, + config = $$.config; + if ($$.hasArcType()) { + return; + } + if (!config.data_selection_enabled) { + return; + } // do nothing if not selectable + $$.dragStart = mouse; + $$.main.select('.' + CLASS.chart).append('rect').attr('class', CLASS.dragarea).style('opacity', 0.1); + $$.dragging = true; +}; + +c3_chart_internal_fn.dragend = function () { + var $$ = this, + config = $$.config; + if ($$.hasArcType()) { + return; + } + if (!config.data_selection_enabled) { + return; + } // do nothing if not selectable + $$.main.select('.' + CLASS.dragarea).transition().duration(100).style('opacity', 0).remove(); + $$.main.selectAll('.' + CLASS.shape).classed(CLASS.INCLUDED, false); + $$.dragging = false; +}; + +c3_chart_internal_fn.getYFormat = function (forArc) { + var $$ = this, + formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat, + formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format; + return function (v, ratio, id) { + var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY; + return format.call($$, v, ratio); + }; +}; +c3_chart_internal_fn.yFormat = function (v) { + var $$ = this, + config = $$.config, + format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat; + return format(v); +}; +c3_chart_internal_fn.y2Format = function (v) { + var $$ = this, + config = $$.config, + format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat; + return format(v); +}; +c3_chart_internal_fn.defaultValueFormat = function (v) { + return isValue(v) ? +v : ""; +}; +c3_chart_internal_fn.defaultArcValueFormat = function (v, ratio) { + return (ratio * 100).toFixed(1) + '%'; +}; +c3_chart_internal_fn.dataLabelFormat = function (targetId) { + var $$ = this, + data_labels = $$.config.data_labels, + format, + defaultFormat = function defaultFormat(v) { + return isValue(v) ? +v : ""; + }; + // find format according to axis id + if (typeof data_labels.format === 'function') { + format = data_labels.format; + } else if (_typeof(data_labels.format) === 'object') { + if (data_labels.format[targetId]) { + format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId]; + } else { + format = function format() { + return ''; + }; + } + } else { + format = defaultFormat; + } + return format; +}; + +c3_chart_internal_fn.initGrid = function () { + var $$ = this, + config = $$.config, + d3 = $$.d3; + $$.grid = $$.main.append('g').attr("clip-path", $$.clipPathForGrid).attr('class', CLASS.grid); + if (config.grid_x_show) { + $$.grid.append("g").attr("class", CLASS.xgrids); + } + if (config.grid_y_show) { + $$.grid.append('g').attr('class', CLASS.ygrids); + } + if (config.grid_focus_show) { + $$.grid.append('g').attr("class", CLASS.xgridFocus).append('line').attr('class', CLASS.xgridFocus); + } + $$.xgrid = d3.selectAll([]); + if (!config.grid_lines_front) { + $$.initGridLines(); + } +}; +c3_chart_internal_fn.initGridLines = function () { + var $$ = this, + d3 = $$.d3; + $$.gridLines = $$.main.append('g').attr("clip-path", $$.clipPathForGrid).attr('class', CLASS.grid + ' ' + CLASS.gridLines); + $$.gridLines.append('g').attr("class", CLASS.xgridLines); + $$.gridLines.append('g').attr('class', CLASS.ygridLines); + $$.xgridLines = d3.selectAll([]); +}; +c3_chart_internal_fn.updateXGrid = function (withoutUpdate) { + var $$ = this, + config = $$.config, + d3 = $$.d3, + xgridData = $$.generateGridData(config.grid_x_type, $$.x), + tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0; + + $$.xgridAttr = config.axis_rotated ? { + 'x1': 0, + 'x2': $$.width, + 'y1': function y1(d) { + return $$.x(d) - tickOffset; + }, + 'y2': function y2(d) { + return $$.x(d) - tickOffset; + } + } : { + 'x1': function x1(d) { + return $$.x(d) + tickOffset; + }, + 'x2': function x2(d) { + return $$.x(d) + tickOffset; + }, + 'y1': 0, + 'y2': $$.height + }; + + $$.xgrid = $$.main.select('.' + CLASS.xgrids).selectAll('.' + CLASS.xgrid).data(xgridData); + $$.xgrid.enter().append('line').attr("class", CLASS.xgrid); + if (!withoutUpdate) { + $$.xgrid.attr($$.xgridAttr).style("opacity", function () { + return +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1') === (config.axis_rotated ? $$.height : 0) ? 0 : 1; + }); + } + $$.xgrid.exit().remove(); +}; + +c3_chart_internal_fn.updateYGrid = function () { + var $$ = this, + config = $$.config, + gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks); + $$.ygrid = $$.main.select('.' + CLASS.ygrids).selectAll('.' + CLASS.ygrid).data(gridValues); + $$.ygrid.enter().append('line').attr('class', CLASS.ygrid); + $$.ygrid.attr("x1", config.axis_rotated ? $$.y : 0).attr("x2", config.axis_rotated ? $$.y : $$.width).attr("y1", config.axis_rotated ? 0 : $$.y).attr("y2", config.axis_rotated ? $$.height : $$.y); + $$.ygrid.exit().remove(); + $$.smoothLines($$.ygrid, 'grid'); +}; + +c3_chart_internal_fn.gridTextAnchor = function (d) { + return d.position ? d.position : "end"; +}; +c3_chart_internal_fn.gridTextDx = function (d) { + return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4; +}; +c3_chart_internal_fn.xGridTextX = function (d) { + return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0; +}; +c3_chart_internal_fn.yGridTextX = function (d) { + return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width; +}; +c3_chart_internal_fn.updateGrid = function (duration) { + var $$ = this, + main = $$.main, + config = $$.config, + xgridLine, + ygridLine, + yv; + + // hide if arc type + $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); + + main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden"); + if (config.grid_x_show) { + $$.updateXGrid(); + } + $$.xgridLines = main.select('.' + CLASS.xgridLines).selectAll('.' + CLASS.xgridLine).data(config.grid_x_lines); + // enter + xgridLine = $$.xgridLines.enter().append('g').attr("class", function (d) { + return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : ''); + }); + xgridLine.append('line').style("opacity", 0); + xgridLine.append('text').attr("text-anchor", $$.gridTextAnchor).attr("transform", config.axis_rotated ? "" : "rotate(-90)").attr('dx', $$.gridTextDx).attr('dy', -5).style("opacity", 0); + // udpate + // done in d3.transition() of the end of this function + // exit + $$.xgridLines.exit().transition().duration(duration).style("opacity", 0).remove(); + + // Y-Grid + if (config.grid_y_show) { + $$.updateYGrid(); + } + $$.ygridLines = main.select('.' + CLASS.ygridLines).selectAll('.' + CLASS.ygridLine).data(config.grid_y_lines); + // enter + ygridLine = $$.ygridLines.enter().append('g').attr("class", function (d) { + return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : ''); + }); + ygridLine.append('line').style("opacity", 0); + ygridLine.append('text').attr("text-anchor", $$.gridTextAnchor).attr("transform", config.axis_rotated ? "rotate(-90)" : "").attr('dx', $$.gridTextDx).attr('dy', -5).style("opacity", 0); + // update + yv = $$.yv.bind($$); + $$.ygridLines.select('line').transition().duration(duration).attr("x1", config.axis_rotated ? yv : 0).attr("x2", config.axis_rotated ? yv : $$.width).attr("y1", config.axis_rotated ? 0 : yv).attr("y2", config.axis_rotated ? $$.height : yv).style("opacity", 1); + $$.ygridLines.select('text').transition().duration(duration).attr("x", config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$)).attr("y", yv).text(function (d) { + return d.text; + }).style("opacity", 1); + // exit + $$.ygridLines.exit().transition().duration(duration).style("opacity", 0).remove(); +}; +c3_chart_internal_fn.redrawGrid = function (withTransition) { + var $$ = this, + config = $$.config, + xv = $$.xv.bind($$), + lines = $$.xgridLines.select('line'), + texts = $$.xgridLines.select('text'); + return [(withTransition ? lines.transition() : lines).attr("x1", config.axis_rotated ? 0 : xv).attr("x2", config.axis_rotated ? $$.width : xv).attr("y1", config.axis_rotated ? xv : 0).attr("y2", config.axis_rotated ? xv : $$.height).style("opacity", 1), (withTransition ? texts.transition() : texts).attr("x", config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$)).attr("y", xv).text(function (d) { + return d.text; + }).style("opacity", 1)]; +}; +c3_chart_internal_fn.showXGridFocus = function (selectedData) { + var $$ = this, + config = $$.config, + dataToShow = selectedData.filter(function (d) { + return d && isValue(d.value); + }), + focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus), + xx = $$.xx.bind($$); + if (!config.tooltip_show) { + return; + } + // Hide when scatter plot exists + if ($$.hasType('scatter') || $$.hasArcType()) { + return; + } + focusEl.style("visibility", "visible").data([dataToShow[0]]).attr(config.axis_rotated ? 'y1' : 'x1', xx).attr(config.axis_rotated ? 'y2' : 'x2', xx); + $$.smoothLines(focusEl, 'grid'); +}; +c3_chart_internal_fn.hideXGridFocus = function () { + this.main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden"); +}; +c3_chart_internal_fn.updateXgridFocus = function () { + var $$ = this, + config = $$.config; + $$.main.select('line.' + CLASS.xgridFocus).attr("x1", config.axis_rotated ? 0 : -10).attr("x2", config.axis_rotated ? $$.width : -10).attr("y1", config.axis_rotated ? -10 : 0).attr("y2", config.axis_rotated ? -10 : $$.height); +}; +c3_chart_internal_fn.generateGridData = function (type, scale) { + var $$ = this, + gridData = [], + xDomain, + firstYear, + lastYear, + i, + tickNum = $$.main.select("." + CLASS.axisX).selectAll('.tick').size(); + if (type === 'year') { + xDomain = $$.getXDomain(); + firstYear = xDomain[0].getFullYear(); + lastYear = xDomain[1].getFullYear(); + for (i = firstYear; i <= lastYear; i++) { + gridData.push(new Date(i + '-01-01 00:00:00')); + } + } else { + gridData = scale.ticks(10); + if (gridData.length > tickNum) { + // use only int + gridData = gridData.filter(function (d) { + return ("" + d).indexOf('.') < 0; + }); + } + } + return gridData; +}; +c3_chart_internal_fn.getGridFilterToRemove = function (params) { + return params ? function (line) { + var found = false; + [].concat(params).forEach(function (param) { + if ('value' in param && line.value === param.value || 'class' in param && line['class'] === param['class']) { + found = true; + } + }); + return found; + } : function () { + return true; + }; +}; +c3_chart_internal_fn.removeGridLines = function (params, forX) { + var $$ = this, + config = $$.config, + toRemove = $$.getGridFilterToRemove(params), + toShow = function toShow(line) { + return !toRemove(line); + }, + classLines = forX ? CLASS.xgridLines : CLASS.ygridLines, + classLine = forX ? CLASS.xgridLine : CLASS.ygridLine; + $$.main.select('.' + classLines).selectAll('.' + classLine).filter(toRemove).transition().duration(config.transition_duration).style('opacity', 0).remove(); + if (forX) { + config.grid_x_lines = config.grid_x_lines.filter(toShow); + } else { + config.grid_y_lines = config.grid_y_lines.filter(toShow); + } +}; + +c3_chart_internal_fn.initEventRect = function () { + var $$ = this; + $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.eventRects).style('fill-opacity', 0); +}; +c3_chart_internal_fn.redrawEventRect = function () { + var $$ = this, + config = $$.config, + eventRectUpdate, + maxDataCountTarget, + isMultipleX = $$.isMultipleX(); + + // rects for mouseover + var eventRects = $$.main.select('.' + CLASS.eventRects).style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null).classed(CLASS.eventRectsMultiple, isMultipleX).classed(CLASS.eventRectsSingle, !isMultipleX); + + // clear old rects + eventRects.selectAll('.' + CLASS.eventRect).remove(); + + // open as public variable + $$.eventRect = eventRects.selectAll('.' + CLASS.eventRect); + + if (isMultipleX) { + eventRectUpdate = $$.eventRect.data([0]); + // enter : only one rect will be added + $$.generateEventRectsForMultipleXs(eventRectUpdate.enter()); + // update + $$.updateEventRect(eventRectUpdate); + // exit : not needed because always only one rect exists + } else { + // Set data and update $$.eventRect + maxDataCountTarget = $$.getMaxDataCountTarget($$.data.targets); + eventRects.datum(maxDataCountTarget ? maxDataCountTarget.values : []); + $$.eventRect = eventRects.selectAll('.' + CLASS.eventRect); + eventRectUpdate = $$.eventRect.data(function (d) { + return d; + }); + // enter + $$.generateEventRectsForSingleX(eventRectUpdate.enter()); + // update + $$.updateEventRect(eventRectUpdate); + // exit + eventRectUpdate.exit().remove(); + } +}; +c3_chart_internal_fn.updateEventRect = function (eventRectUpdate) { + var $$ = this, + config = $$.config, + x, + y, + w, + h, + rectW, + rectX; + + // set update selection if null + eventRectUpdate = eventRectUpdate || $$.eventRect.data(function (d) { + return d; + }); + + if ($$.isMultipleX()) { + // TODO: rotated not supported yet + x = 0; + y = 0; + w = $$.width; + h = $$.height; + } else { + if (($$.isCustomX() || $$.isTimeSeries()) && !$$.isCategorized()) { + + // update index for x that is used by prevX and nextX + $$.updateXs(); + + rectW = function rectW(d) { + var prevX = $$.getPrevX(d.index), + nextX = $$.getNextX(d.index); + + // if there this is a single data point make the eventRect full width (or height) + if (prevX === null && nextX === null) { + return config.axis_rotated ? $$.height : $$.width; + } + + if (prevX === null) { + prevX = $$.x.domain()[0]; + } + if (nextX === null) { + nextX = $$.x.domain()[1]; + } + + return Math.max(0, ($$.x(nextX) - $$.x(prevX)) / 2); + }; + rectX = function rectX(d) { + var prevX = $$.getPrevX(d.index), + nextX = $$.getNextX(d.index), + thisX = $$.data.xs[d.id][d.index]; + + // if there this is a single data point position the eventRect at 0 + if (prevX === null && nextX === null) { + return 0; + } + + if (prevX === null) { + prevX = $$.x.domain()[0]; + } + + return ($$.x(thisX) + $$.x(prevX)) / 2; + }; + } else { + rectW = $$.getEventRectWidth(); + rectX = function rectX(d) { + return $$.x(d.x) - rectW / 2; + }; + } + x = config.axis_rotated ? 0 : rectX; + y = config.axis_rotated ? rectX : 0; + w = config.axis_rotated ? $$.width : rectW; + h = config.axis_rotated ? rectW : $$.height; + } + + eventRectUpdate.attr('class', $$.classEvent.bind($$)).attr("x", x).attr("y", y).attr("width", w).attr("height", h); +}; +c3_chart_internal_fn.generateEventRectsForSingleX = function (eventRectEnter) { + var $$ = this, + d3 = $$.d3, + config = $$.config; + eventRectEnter.append("rect").attr("class", $$.classEvent.bind($$)).style("cursor", config.data_selection_enabled && config.data_selection_grouped ? "pointer" : null).on('mouseover', function (d) { + var index = d.index; + + if ($$.dragging || $$.flowing) { + return; + } // do nothing while dragging/flowing + if ($$.hasArcType()) { + return; + } + + // Expand shapes for selection + if (config.point_focus_expand_enabled) { + $$.expandCircles(index, null, true); + } + $$.expandBars(index, null, true); + + // Call event handler + $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) { + config.data_onmouseover.call($$.api, d); + }); + }).on('mouseout', function (d) { + var index = d.index; + if (!$$.config) { + return; + } // chart is destroyed + if ($$.hasArcType()) { + return; + } + $$.hideXGridFocus(); + $$.hideTooltip(); + // Undo expanded shapes + $$.unexpandCircles(); + $$.unexpandBars(); + // Call event handler + $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) { + config.data_onmouseout.call($$.api, d); + }); + }).on('mousemove', function (d) { + var selectedData, + index = d.index, + eventRect = $$.svg.select('.' + CLASS.eventRect + '-' + index); + + if ($$.dragging || $$.flowing) { + return; + } // do nothing while dragging/flowing + if ($$.hasArcType()) { + return; + } + + if ($$.isStepType(d) && $$.config.line_step_type === 'step-after' && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) { + index -= 1; + } + + // Show tooltip + selectedData = $$.filterTargetsToShow($$.data.targets).map(function (t) { + return $$.addName($$.getValueOnIndex(t.values, index)); + }); + + if (config.tooltip_grouped) { + $$.showTooltip(selectedData, this); + $$.showXGridFocus(selectedData); + } + + if (config.tooltip_grouped && (!config.data_selection_enabled || config.data_selection_grouped)) { + return; + } + + $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function () { + d3.select(this).classed(CLASS.EXPANDED, true); + if (config.data_selection_enabled) { + eventRect.style('cursor', config.data_selection_grouped ? 'pointer' : null); + } + if (!config.tooltip_grouped) { + $$.hideXGridFocus(); + $$.hideTooltip(); + if (!config.data_selection_grouped) { + $$.unexpandCircles(index); + $$.unexpandBars(index); + } + } + }).filter(function (d) { + return $$.isWithinShape(this, d); + }).each(function (d) { + if (config.data_selection_enabled && (config.data_selection_grouped || config.data_selection_isselectable(d))) { + eventRect.style('cursor', 'pointer'); + } + if (!config.tooltip_grouped) { + $$.showTooltip([d], this); + $$.showXGridFocus([d]); + if (config.point_focus_expand_enabled) { + $$.expandCircles(index, d.id, true); + } + $$.expandBars(index, d.id, true); + } + }); + }).on('click', function (d) { + var index = d.index; + if ($$.hasArcType() || !$$.toggleShape) { + return; + } + if ($$.cancelClick) { + $$.cancelClick = false; + return; + } + if ($$.isStepType(d) && config.line_step_type === 'step-after' && d3.mouse(this)[0] < $$.x($$.getXValue(d.id, index))) { + index -= 1; + } + $$.main.selectAll('.' + CLASS.shape + '-' + index).each(function (d) { + if (config.data_selection_grouped || $$.isWithinShape(this, d)) { + $$.toggleShape(this, d, index); + $$.config.data_onclick.call($$.api, d, this); + } + }); + }).call(config.data_selection_draggable && $$.drag ? d3.behavior.drag().origin(Object).on('drag', function () { + $$.drag(d3.mouse(this)); + }).on('dragstart', function () { + $$.dragstart(d3.mouse(this)); + }).on('dragend', function () { + $$.dragend(); + }) : function () {}); +}; + +c3_chart_internal_fn.generateEventRectsForMultipleXs = function (eventRectEnter) { + var $$ = this, + d3 = $$.d3, + config = $$.config; + + function mouseout() { + $$.svg.select('.' + CLASS.eventRect).style('cursor', null); + $$.hideXGridFocus(); + $$.hideTooltip(); + $$.unexpandCircles(); + $$.unexpandBars(); + } + + eventRectEnter.append('rect').attr('x', 0).attr('y', 0).attr('width', $$.width).attr('height', $$.height).attr('class', CLASS.eventRect).on('mouseout', function () { + if (!$$.config) { + return; + } // chart is destroyed + if ($$.hasArcType()) { + return; + } + mouseout(); + }).on('mousemove', function () { + var targetsToShow = $$.filterTargetsToShow($$.data.targets); + var mouse, closest, sameXData, selectedData; + + if ($$.dragging) { + return; + } // do nothing when dragging + if ($$.hasArcType(targetsToShow)) { + return; + } + + mouse = d3.mouse(this); + closest = $$.findClosestFromTargets(targetsToShow, mouse); + + if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id)) { + config.data_onmouseout.call($$.api, $$.mouseover); + $$.mouseover = undefined; + } + + if (!closest) { + mouseout(); + return; + } + + if ($$.isScatterType(closest) || !config.tooltip_grouped) { + sameXData = [closest]; + } else { + sameXData = $$.filterByX(targetsToShow, closest.x); + } + + // show tooltip when cursor is close to some point + selectedData = sameXData.map(function (d) { + return $$.addName(d); + }); + $$.showTooltip(selectedData, this); + + // expand points + if (config.point_focus_expand_enabled) { + $$.expandCircles(closest.index, closest.id, true); + } + $$.expandBars(closest.index, closest.id, true); + + // Show xgrid focus line + $$.showXGridFocus(selectedData); + + // Show cursor as pointer if point is close to mouse position + if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) { + $$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer'); + if (!$$.mouseover) { + config.data_onmouseover.call($$.api, closest); + $$.mouseover = closest; + } + } + }).on('click', function () { + var targetsToShow = $$.filterTargetsToShow($$.data.targets); + var mouse, closest; + if ($$.hasArcType(targetsToShow)) { + return; + } + + mouse = d3.mouse(this); + closest = $$.findClosestFromTargets(targetsToShow, mouse); + if (!closest) { + return; + } + // select if selection enabled + if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) { + $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id)).selectAll('.' + CLASS.shape + '-' + closest.index).each(function () { + if (config.data_selection_grouped || $$.isWithinShape(this, closest)) { + $$.toggleShape(this, closest, closest.index); + $$.config.data_onclick.call($$.api, closest, this); + } + }); + } + }).call(config.data_selection_draggable && $$.drag ? d3.behavior.drag().origin(Object).on('drag', function () { + $$.drag(d3.mouse(this)); + }).on('dragstart', function () { + $$.dragstart(d3.mouse(this)); + }).on('dragend', function () { + $$.dragend(); + }) : function () {}); +}; +c3_chart_internal_fn.dispatchEvent = function (type, index, mouse) { + var $$ = this, + selector = '.' + CLASS.eventRect + (!$$.isMultipleX() ? '-' + index : ''), + eventRect = $$.main.select(selector).node(), + box = eventRect.getBoundingClientRect(), + x = box.left + (mouse ? mouse[0] : 0), + y = box.top + (mouse ? mouse[1] : 0), + event = document.createEvent("MouseEvents"); + + event.initMouseEvent(type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null); + eventRect.dispatchEvent(event); +}; + +c3_chart_internal_fn.initLegend = function () { + var $$ = this; + $$.legendItemTextBox = {}; + $$.legendHasRendered = false; + $$.legend = $$.svg.append("g").attr("transform", $$.getTranslate('legend')); + if (!$$.config.legend_show) { + $$.legend.style('visibility', 'hidden'); + $$.hiddenLegendIds = $$.mapToIds($$.data.targets); + return; + } + // MEMO: call here to update legend box and tranlate for all + // MEMO: translate will be upated by this, so transform not needed in updateLegend() + $$.updateLegendWithDefaults(); +}; +c3_chart_internal_fn.updateLegendWithDefaults = function () { + var $$ = this; + $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false }); +}; +c3_chart_internal_fn.updateSizeForLegend = function (legendHeight, legendWidth) { + var $$ = this, + config = $$.config, + insetLegendPosition = { + top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y, + left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5 + }; + + $$.margin3 = { + top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight, + right: NaN, + bottom: 0, + left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0 + }; +}; +c3_chart_internal_fn.transformLegend = function (withTransition) { + var $$ = this; + (withTransition ? $$.legend.transition() : $$.legend).attr("transform", $$.getTranslate('legend')); +}; +c3_chart_internal_fn.updateLegendStep = function (step) { + this.legendStep = step; +}; +c3_chart_internal_fn.updateLegendItemWidth = function (w) { + this.legendItemWidth = w; +}; +c3_chart_internal_fn.updateLegendItemHeight = function (h) { + this.legendItemHeight = h; +}; +c3_chart_internal_fn.getLegendWidth = function () { + var $$ = this; + return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0; +}; +c3_chart_internal_fn.getLegendHeight = function () { + var $$ = this, + h = 0; + if ($$.config.legend_show) { + if ($$.isLegendRight) { + h = $$.currentHeight; + } else { + h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1); + } + } + return h; +}; +c3_chart_internal_fn.opacityForLegend = function (legendItem) { + return legendItem.classed(CLASS.legendItemHidden) ? null : 1; +}; +c3_chart_internal_fn.opacityForUnfocusedLegend = function (legendItem) { + return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3; +}; +c3_chart_internal_fn.toggleFocusLegend = function (targetIds, focus) { + var $$ = this; + targetIds = $$.mapToTargetIds(targetIds); + $$.legend.selectAll('.' + CLASS.legendItem).filter(function (id) { + return targetIds.indexOf(id) >= 0; + }).classed(CLASS.legendItemFocused, focus).transition().duration(100).style('opacity', function () { + var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend; + return opacity.call($$, $$.d3.select(this)); + }); +}; +c3_chart_internal_fn.revertLegend = function () { + var $$ = this, + d3 = $$.d3; + $$.legend.selectAll('.' + CLASS.legendItem).classed(CLASS.legendItemFocused, false).transition().duration(100).style('opacity', function () { + return $$.opacityForLegend(d3.select(this)); + }); +}; +c3_chart_internal_fn.showLegend = function (targetIds) { + var $$ = this, + config = $$.config; + if (!config.legend_show) { + config.legend_show = true; + $$.legend.style('visibility', 'visible'); + if (!$$.legendHasRendered) { + $$.updateLegendWithDefaults(); + } + } + $$.removeHiddenLegendIds(targetIds); + $$.legend.selectAll($$.selectorLegends(targetIds)).style('visibility', 'visible').transition().style('opacity', function () { + return $$.opacityForLegend($$.d3.select(this)); + }); +}; +c3_chart_internal_fn.hideLegend = function (targetIds) { + var $$ = this, + config = $$.config; + if (config.legend_show && isEmpty(targetIds)) { + config.legend_show = false; + $$.legend.style('visibility', 'hidden'); + } + $$.addHiddenLegendIds(targetIds); + $$.legend.selectAll($$.selectorLegends(targetIds)).style('opacity', 0).style('visibility', 'hidden'); +}; +c3_chart_internal_fn.clearLegendItemTextBoxCache = function () { + this.legendItemTextBox = {}; +}; +c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { + var $$ = this, + config = $$.config; + var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile; + var paddingTop = 4, + paddingRight = 10, + maxWidth = 0, + maxHeight = 0, + posMin = 10, + tileWidth = config.legend_item_tile_width + 5; + var l, + totalLength = 0, + offsets = {}, + widths = {}, + heights = {}, + margins = [0], + steps = {}, + step = 0; + var withTransition, withTransitionForTransform; + var texts, rects, tiles, background; + + // Skip elements when their name is set to null + targetIds = targetIds.filter(function (id) { + return !isDefined(config.data_names[id]) || config.data_names[id] !== null; + }); + + options = options || {}; + withTransition = getOption(options, "withTransition", true); + withTransitionForTransform = getOption(options, "withTransitionForTransform", true); + + function getTextBox(textElement, id) { + if (!$$.legendItemTextBox[id]) { + $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem, textElement); + } + return $$.legendItemTextBox[id]; + } + + function updatePositions(textElement, id, index) { + var reset = index === 0, + isLast = index === targetIds.length - 1, + box = getTextBox(textElement, id), + itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, + itemHeight = box.height + paddingTop, + itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, + areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), + margin, + maxLength; + + // MEMO: care about condifion of step, totalLength + function updateValues(id, withoutStep) { + if (!withoutStep) { + margin = (areaLength - totalLength - itemLength) / 2; + if (margin < posMin) { + margin = (areaLength - itemLength) / 2; + totalLength = 0; + step++; + } + } + steps[id] = step; + margins[step] = $$.isLegendInset ? 10 : margin; + offsets[id] = totalLength; + totalLength += itemLength; + } + + if (reset) { + totalLength = 0; + step = 0; + maxWidth = 0; + maxHeight = 0; + } + + if (config.legend_show && !$$.isLegendToShow(id)) { + widths[id] = heights[id] = steps[id] = offsets[id] = 0; + return; + } + + widths[id] = itemWidth; + heights[id] = itemHeight; + + if (!maxWidth || itemWidth >= maxWidth) { + maxWidth = itemWidth; + } + if (!maxHeight || itemHeight >= maxHeight) { + maxHeight = itemHeight; + } + maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth; + + if (config.legend_equally) { + Object.keys(widths).forEach(function (id) { + widths[id] = maxWidth; + }); + Object.keys(heights).forEach(function (id) { + heights[id] = maxHeight; + }); + margin = (areaLength - maxLength * targetIds.length) / 2; + if (margin < posMin) { + totalLength = 0; + step = 0; + targetIds.forEach(function (id) { + updateValues(id); + }); + } else { + updateValues(id, true); + } + } else { + updateValues(id); + } + } + + if ($$.isLegendInset) { + step = config.legend_inset_step ? config.legend_inset_step : targetIds.length; + $$.updateLegendStep(step); + } + + if ($$.isLegendRight) { + xForLegend = function xForLegend(id) { + return maxWidth * steps[id]; + }; + yForLegend = function yForLegend(id) { + return margins[steps[id]] + offsets[id]; + }; + } else if ($$.isLegendInset) { + xForLegend = function xForLegend(id) { + return maxWidth * steps[id] + 10; + }; + yForLegend = function yForLegend(id) { + return margins[steps[id]] + offsets[id]; + }; + } else { + xForLegend = function xForLegend(id) { + return margins[steps[id]] + offsets[id]; + }; + yForLegend = function yForLegend(id) { + return maxHeight * steps[id]; + }; + } + xForLegendText = function xForLegendText(id, i) { + return xForLegend(id, i) + 4 + config.legend_item_tile_width; + }; + yForLegendText = function yForLegendText(id, i) { + return yForLegend(id, i) + 9; + }; + xForLegendRect = function xForLegendRect(id, i) { + return xForLegend(id, i); + }; + yForLegendRect = function yForLegendRect(id, i) { + return yForLegend(id, i) - 5; + }; + x1ForLegendTile = function x1ForLegendTile(id, i) { + return xForLegend(id, i) - 2; + }; + x2ForLegendTile = function x2ForLegendTile(id, i) { + return xForLegend(id, i) - 2 + config.legend_item_tile_width; + }; + yForLegendTile = function yForLegendTile(id, i) { + return yForLegend(id, i) + 4; + }; + + // Define g for legend area + l = $$.legend.selectAll('.' + CLASS.legendItem).data(targetIds).enter().append('g').attr('class', function (id) { + return $$.generateClass(CLASS.legendItem, id); + }).style('visibility', function (id) { + return $$.isLegendToShow(id) ? 'visible' : 'hidden'; + }).style('cursor', 'pointer').on('click', function (id) { + if (config.legend_item_onclick) { + config.legend_item_onclick.call($$, id); + } else { + if ($$.d3.event.altKey) { + $$.api.hide(); + $$.api.show(id); + } else { + $$.api.toggle(id); + $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert(); + } + } + }).on('mouseover', function (id) { + if (config.legend_item_onmouseover) { + config.legend_item_onmouseover.call($$, id); + } else { + $$.d3.select(this).classed(CLASS.legendItemFocused, true); + if (!$$.transiting && $$.isTargetToShow(id)) { + $$.api.focus(id); + } + } + }).on('mouseout', function (id) { + if (config.legend_item_onmouseout) { + config.legend_item_onmouseout.call($$, id); + } else { + $$.d3.select(this).classed(CLASS.legendItemFocused, false); + $$.api.revert(); + } + }); + l.append('text').text(function (id) { + return isDefined(config.data_names[id]) ? config.data_names[id] : id; + }).each(function (id, i) { + updatePositions(this, id, i); + }).style("pointer-events", "none").attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200).attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText); + l.append('rect').attr("class", CLASS.legendItemEvent).style('fill-opacity', 0).attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200).attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); + l.append('line').attr('class', CLASS.legendItemTile).style('stroke', $$.color).style("pointer-events", "none").attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200).attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile).attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200).attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile).attr('stroke-width', config.legend_item_tile_height); + + // Set background for inset legend + background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); + if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) { + background = $$.legend.insert('g', '.' + CLASS.legendItem).attr("class", CLASS.legendBackground).append('rect'); + } + + texts = $$.legend.selectAll('text').data(targetIds).text(function (id) { + return isDefined(config.data_names[id]) ? config.data_names[id] : id; + } // MEMO: needed for update + ).each(function (id, i) { + updatePositions(this, id, i); + }); + (withTransition ? texts.transition() : texts).attr('x', xForLegendText).attr('y', yForLegendText); + + rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds); + (withTransition ? rects.transition() : rects).attr('width', function (id) { + return widths[id]; + }).attr('height', function (id) { + return heights[id]; + }).attr('x', xForLegendRect).attr('y', yForLegendRect); + + tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile).data(targetIds); + (withTransition ? tiles.transition() : tiles).style('stroke', $$.color).attr('x1', x1ForLegendTile).attr('y1', yForLegendTile).attr('x2', x2ForLegendTile).attr('y2', yForLegendTile); + + if (background) { + (withTransition ? background.transition() : background).attr('height', $$.getLegendHeight() - 12).attr('width', maxWidth * (step + 1) + 10); + } + + // toggle legend state + $$.legend.selectAll('.' + CLASS.legendItem).classed(CLASS.legendItemHidden, function (id) { + return !$$.isTargetToShow(id); + }); + + // Update all to reflect change of legend + $$.updateLegendItemWidth(maxWidth); + $$.updateLegendItemHeight(maxHeight); + $$.updateLegendStep(step); + // Update size and scale + $$.updateSizes(); + $$.updateScales(); + $$.updateSvgSize(); + // Update g positions + $$.transformAll(withTransitionForTransform, transitions); + $$.legendHasRendered = true; +}; + +c3_chart_internal_fn.initRegion = function () { + var $$ = this; + $$.region = $$.main.append('g').attr("clip-path", $$.clipPath).attr("class", CLASS.regions); +}; +c3_chart_internal_fn.updateRegion = function (duration) { + var $$ = this, + config = $$.config; + + // hide if arc type + $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); + + $$.mainRegion = $$.main.select('.' + CLASS.regions).selectAll('.' + CLASS.region).data(config.regions); + $$.mainRegion.enter().append('g').append('rect').style("fill-opacity", 0); + $$.mainRegion.attr('class', $$.classRegion.bind($$)); + $$.mainRegion.exit().transition().duration(duration).style("opacity", 0).remove(); +}; +c3_chart_internal_fn.redrawRegion = function (withTransition) { + var $$ = this, + regions = $$.mainRegion.selectAll('rect').each(function () { + // data is binded to g and it's not transferred to rect (child node) automatically, + // then data of each rect has to be updated manually. + // TODO: there should be more efficient way to solve this? + var parentData = $$.d3.select(this.parentNode).datum(); + $$.d3.select(this).datum(parentData); + }), + x = $$.regionX.bind($$), + y = $$.regionY.bind($$), + w = $$.regionWidth.bind($$), + h = $$.regionHeight.bind($$); + return [(withTransition ? regions.transition() : regions).attr("x", x).attr("y", y).attr("width", w).attr("height", h).style("fill-opacity", function (d) { + return isValue(d.opacity) ? d.opacity : 0.1; + })]; +}; +c3_chart_internal_fn.regionX = function (d) { + var $$ = this, + config = $$.config, + xPos, + yScale = d.axis === 'y' ? $$.y : $$.y2; + if (d.axis === 'y' || d.axis === 'y2') { + xPos = config.axis_rotated ? 'start' in d ? yScale(d.start) : 0 : 0; + } else { + xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0; + } + return xPos; +}; +c3_chart_internal_fn.regionY = function (d) { + var $$ = this, + config = $$.config, + yPos, + yScale = d.axis === 'y' ? $$.y : $$.y2; + if (d.axis === 'y' || d.axis === 'y2') { + yPos = config.axis_rotated ? 0 : 'end' in d ? yScale(d.end) : 0; + } else { + yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0; + } + return yPos; +}; +c3_chart_internal_fn.regionWidth = function (d) { + var $$ = this, + config = $$.config, + start = $$.regionX(d), + end, + yScale = d.axis === 'y' ? $$.y : $$.y2; + if (d.axis === 'y' || d.axis === 'y2') { + end = config.axis_rotated ? 'end' in d ? yScale(d.end) : $$.width : $$.width; + } else { + end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width; + } + return end < start ? 0 : end - start; +}; +c3_chart_internal_fn.regionHeight = function (d) { + var $$ = this, + config = $$.config, + start = this.regionY(d), + end, + yScale = d.axis === 'y' ? $$.y : $$.y2; + if (d.axis === 'y' || d.axis === 'y2') { + end = config.axis_rotated ? $$.height : 'start' in d ? yScale(d.start) : $$.height; + } else { + end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height; + } + return end < start ? 0 : end - start; +}; +c3_chart_internal_fn.isRegionOnX = function (d) { + return !d.axis || d.axis === 'x'; +}; + +c3_chart_internal_fn.getScale = function (min, max, forTimeseries) { + return (forTimeseries ? this.d3.time.scale() : this.d3.scale.linear()).range([min, max]); +}; +c3_chart_internal_fn.getX = function (min, max, domain, offset) { + var $$ = this, + scale = $$.getScale(min, max, $$.isTimeSeries()), + _scale = domain ? scale.domain(domain) : scale, + key; + // Define customized scale if categorized axis + if ($$.isCategorized()) { + offset = offset || function () { + return 0; + }; + scale = function scale(d, raw) { + var v = _scale(d) + offset(d); + return raw ? v : Math.ceil(v); + }; + } else { + scale = function scale(d, raw) { + var v = _scale(d); + return raw ? v : Math.ceil(v); + }; + } + // define functions + for (key in _scale) { + scale[key] = _scale[key]; + } + scale.orgDomain = function () { + return _scale.domain(); + }; + // define custom domain() for categorized axis + if ($$.isCategorized()) { + scale.domain = function (domain) { + if (!arguments.length) { + domain = this.orgDomain(); + return [domain[0], domain[1] + 1]; + } + _scale.domain(domain); + return scale; + }; + } + return scale; +}; +c3_chart_internal_fn.getY = function (min, max, domain) { + var scale = this.getScale(min, max, this.isTimeSeriesY()); + if (domain) { + scale.domain(domain); + } + return scale; +}; +c3_chart_internal_fn.getYScale = function (id) { + return this.axis.getId(id) === 'y2' ? this.y2 : this.y; +}; +c3_chart_internal_fn.getSubYScale = function (id) { + return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY; +}; +c3_chart_internal_fn.updateScales = function () { + var $$ = this, + config = $$.config, + forInit = !$$.x; + // update edges + $$.xMin = config.axis_rotated ? 1 : 0; + $$.xMax = config.axis_rotated ? $$.height : $$.width; + $$.yMin = config.axis_rotated ? 0 : $$.height; + $$.yMax = config.axis_rotated ? $$.width : 1; + $$.subXMin = $$.xMin; + $$.subXMax = $$.xMax; + $$.subYMin = config.axis_rotated ? 0 : $$.height2; + $$.subYMax = config.axis_rotated ? $$.width2 : 1; + // update scales + $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () { + return $$.xAxis.tickOffset(); + }); + $$.y = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y_default : $$.y.domain()); + $$.y2 = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y2_default : $$.y2.domain()); + $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function (d) { + return d % 1 ? 0 : $$.subXAxis.tickOffset(); + }); + $$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y_default : $$.subY.domain()); + $$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain()); + // update axes + $$.xAxisTickFormat = $$.axis.getXAxisTickFormat(); + $$.xAxisTickValues = $$.axis.getXAxisTickValues(); + $$.yAxisTickValues = $$.axis.getYAxisTickValues(); + $$.y2AxisTickValues = $$.axis.getY2AxisTickValues(); + + $$.xAxis = $$.axis.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); + $$.subXAxis = $$.axis.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); + $$.yAxis = $$.axis.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, config.axis_y_tick_outer); + $$.y2Axis = $$.axis.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, config.axis_y2_tick_outer); + + // Set initialized scales to brush and zoom + if (!forInit) { + if ($$.brush) { + $$.brush.scale($$.subX); + } + if (config.zoom_enabled) { + $$.zoom.scale($$.x); + } + } + // update for arc + if ($$.updateArc) { + $$.updateArc(); + } +}; + +c3_chart_internal_fn.selectPoint = function (target, d, i) { + var $$ = this, + config = $$.config, + cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$), + cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$), + r = $$.pointSelectR.bind($$); + config.data_onselected.call($$.api, d, target.node()); + // add selected-circle on low layer g + $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i).data([d]).enter().append('circle').attr("class", function () { + return $$.generateClass(CLASS.selectedCircle, i); + }).attr("cx", cx).attr("cy", cy).attr("stroke", function () { + return $$.color(d); + }).attr("r", function (d) { + return $$.pointSelectR(d) * 1.4; + }).transition().duration(100).attr("r", r); +}; +c3_chart_internal_fn.unselectPoint = function (target, d, i) { + var $$ = this; + $$.config.data_onunselected.call($$.api, d, target.node()); + // remove selected-circle from low layer g + $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i).transition().duration(100).attr('r', 0).remove(); +}; +c3_chart_internal_fn.togglePoint = function (selected, target, d, i) { + selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i); +}; +c3_chart_internal_fn.selectPath = function (target, d) { + var $$ = this; + $$.config.data_onselected.call($$, d, target.node()); + if ($$.config.interaction_brighten) { + target.transition().duration(100).style("fill", function () { + return $$.d3.rgb($$.color(d)).brighter(0.75); + }); + } +}; +c3_chart_internal_fn.unselectPath = function (target, d) { + var $$ = this; + $$.config.data_onunselected.call($$, d, target.node()); + if ($$.config.interaction_brighten) { + target.transition().duration(100).style("fill", function () { + return $$.color(d); + }); + } +}; +c3_chart_internal_fn.togglePath = function (selected, target, d, i) { + selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i); +}; +c3_chart_internal_fn.getToggle = function (that, d) { + var $$ = this, + toggle; + if (that.nodeName === 'circle') { + if ($$.isStepType(d)) { + // circle is hidden in step chart, so treat as within the click area + toggle = function toggle() {}; // TODO: how to select step chart? + } else { + toggle = $$.togglePoint; + } + } else if (that.nodeName === 'path') { + toggle = $$.togglePath; + } + return toggle; +}; +c3_chart_internal_fn.toggleShape = function (that, d, i) { + var $$ = this, + d3 = $$.d3, + config = $$.config, + shape = d3.select(that), + isSelected = shape.classed(CLASS.SELECTED), + toggle = $$.getToggle(that, d).bind($$); + + if (config.data_selection_enabled && config.data_selection_isselectable(d)) { + if (!config.data_selection_multiple) { + $$.main.selectAll('.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : "")).selectAll('.' + CLASS.shape).each(function (d, i) { + var shape = d3.select(this); + if (shape.classed(CLASS.SELECTED)) { + toggle(false, shape.classed(CLASS.SELECTED, false), d, i); + } + }); + } + shape.classed(CLASS.SELECTED, !isSelected); + toggle(!isSelected, shape, d, i); + } +}; + +c3_chart_internal_fn.initBar = function () { + var $$ = this; + $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartBars); +}; +c3_chart_internal_fn.updateTargetsForBar = function (targets) { + var $$ = this, + config = $$.config, + mainBarUpdate, + mainBarEnter, + classChartBar = $$.classChartBar.bind($$), + classBars = $$.classBars.bind($$), + classFocus = $$.classFocus.bind($$); + mainBarUpdate = $$.main.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar).data(targets).attr('class', function (d) { + return classChartBar(d) + classFocus(d); + }); + mainBarEnter = mainBarUpdate.enter().append('g').attr('class', classChartBar).style("pointer-events", "none"); + // Bars for each data + mainBarEnter.append('g').attr("class", classBars).style("cursor", function (d) { + return config.data_selection_isselectable(d) ? "pointer" : null; + }); +}; +c3_chart_internal_fn.updateBar = function (durationForExit) { + var $$ = this, + barData = $$.barData.bind($$), + classBar = $$.classBar.bind($$), + initialOpacity = $$.initialOpacity.bind($$), + color = function color(d) { + return $$.color(d.id); + }; + $$.mainBar = $$.main.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar).data(barData); + $$.mainBar.enter().append('path').attr("class", classBar).style("stroke", color).style("fill", color); + $$.mainBar.style("opacity", initialOpacity); + $$.mainBar.exit().transition().duration(durationForExit).remove(); +}; +c3_chart_internal_fn.redrawBar = function (drawBar, withTransition) { + return [(withTransition ? this.mainBar.transition(Math.random().toString()) : this.mainBar).attr('d', drawBar).style("stroke", this.color).style("fill", this.color).style("opacity", 1)]; +}; +c3_chart_internal_fn.getBarW = function (axis, barTargetsNum) { + var $$ = this, + config = $$.config, + w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? axis.tickInterval() * config.bar_width_ratio / barTargetsNum : 0; + return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w; +}; +c3_chart_internal_fn.getBars = function (i, id) { + var $$ = this; + return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : '')); +}; +c3_chart_internal_fn.expandBars = function (i, id, reset) { + var $$ = this; + if (reset) { + $$.unexpandBars(); + } + $$.getBars(i, id).classed(CLASS.EXPANDED, true); +}; +c3_chart_internal_fn.unexpandBars = function (i) { + var $$ = this; + $$.getBars(i).classed(CLASS.EXPANDED, false); +}; +c3_chart_internal_fn.generateDrawBar = function (barIndices, isSub) { + var $$ = this, + config = $$.config, + getPoints = $$.generateGetBarPoints(barIndices, isSub); + return function (d, i) { + // 4 points that make a bar + var points = getPoints(d, i); + + // switch points if axis is rotated, not applicable for sub chart + var indexX = config.axis_rotated ? 1 : 0; + var indexY = config.axis_rotated ? 0 : 1; + + var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + 'z'; + + return path; + }; +}; +c3_chart_internal_fn.generateGetBarPoints = function (barIndices, isSub) { + var $$ = this, + axis = isSub ? $$.subXAxis : $$.xAxis, + barTargetsNum = barIndices.__max__ + 1, + barW = $$.getBarW(axis, barTargetsNum), + barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub), + barY = $$.getShapeY(!!isSub), + barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub), + barSpaceOffset = barW * ($$.config.bar_space / 2), + yScale = isSub ? $$.getSubYScale : $$.getYScale; + return function (d, i) { + var y0 = yScale.call($$, d.id)(0), + offset = barOffset(d, i) || y0, + // offset is for stacked bar chart + posX = barX(d), + posY = barY(d); + // fix posY not to overflow opposite quadrant + if ($$.config.axis_rotated) { + if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) { + posY = y0; + } + } + // 4 points that make a bar + return [[posX + barSpaceOffset, offset], [posX + barSpaceOffset, posY - (y0 - offset)], [posX + barW - barSpaceOffset, posY - (y0 - offset)], [posX + barW - barSpaceOffset, offset]]; + }; +}; +c3_chart_internal_fn.isWithinBar = function (that) { + var mouse = this.d3.mouse(that), + box = that.getBoundingClientRect(), + seg0 = that.pathSegList.getItem(0), + seg1 = that.pathSegList.getItem(1), + x = Math.min(seg0.x, seg1.x), + y = Math.min(seg0.y, seg1.y), + w = box.width, + h = box.height, + offset = 2, + sx = x - offset, + ex = x + w + offset, + sy = y + h + offset, + ey = y - offset; + return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy; +}; + +c3_chart_internal_fn.getShapeIndices = function (typeFilter) { + var $$ = this, + config = $$.config, + indices = {}, + i = 0, + j, + k; + $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach(function (d) { + for (j = 0; j < config.data_groups.length; j++) { + if (config.data_groups[j].indexOf(d.id) < 0) { + continue; + } + for (k = 0; k < config.data_groups[j].length; k++) { + if (config.data_groups[j][k] in indices) { + indices[d.id] = indices[config.data_groups[j][k]]; + break; + } + } + } + if (isUndefined(indices[d.id])) { + indices[d.id] = i++; + } + }); + indices.__max__ = i - 1; + return indices; +}; +c3_chart_internal_fn.getShapeX = function (offset, targetsNum, indices, isSub) { + var $$ = this, + scale = isSub ? $$.subX : $$.x; + return function (d) { + var index = d.id in indices ? indices[d.id] : 0; + return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0; + }; +}; +c3_chart_internal_fn.getShapeY = function (isSub) { + var $$ = this; + return function (d) { + var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id); + return scale(d.value); + }; +}; +c3_chart_internal_fn.getShapeOffset = function (typeFilter, indices, isSub) { + var $$ = this, + targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))), + targetIds = targets.map(function (t) { + return t.id; + }); + return function (d, i) { + var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id), + y0 = scale(0), + offset = y0; + targets.forEach(function (t) { + var values = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values; + if (t.id === d.id || indices[t.id] !== indices[d.id]) { + return; + } + if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) { + // check if the x values line up + if (typeof values[i] === 'undefined' || +values[i].x !== +d.x) { + // "+" for timeseries + // if not, try to find the value that does line up + i = -1; + values.forEach(function (v, j) { + if (v.x === d.x) { + i = j; + } + }); + } + if (i in values && values[i].value * d.value >= 0) { + offset += scale(values[i].value) - y0; + } + } + }); + return offset; + }; +}; +c3_chart_internal_fn.isWithinShape = function (that, d) { + var $$ = this, + shape = $$.d3.select(that), + isWithin; + if (!$$.isTargetToShow(d.id)) { + isWithin = false; + } else if (that.nodeName === 'circle') { + isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5); + } else if (that.nodeName === 'path') { + isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar(that) : true; + } + return isWithin; +}; + +c3_chart_internal_fn.getInterpolate = function (d) { + var $$ = this, + interpolation = $$.isInterpolationType($$.config.spline_interpolation_type) ? $$.config.spline_interpolation_type : 'cardinal'; + return $$.isSplineType(d) ? interpolation : $$.isStepType(d) ? $$.config.line_step_type : "linear"; +}; + +c3_chart_internal_fn.initLine = function () { + var $$ = this; + $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartLines); +}; +c3_chart_internal_fn.updateTargetsForLine = function (targets) { + var $$ = this, + config = $$.config, + mainLineUpdate, + mainLineEnter, + classChartLine = $$.classChartLine.bind($$), + classLines = $$.classLines.bind($$), + classAreas = $$.classAreas.bind($$), + classCircles = $$.classCircles.bind($$), + classFocus = $$.classFocus.bind($$); + mainLineUpdate = $$.main.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine).data(targets).attr('class', function (d) { + return classChartLine(d) + classFocus(d); + }); + mainLineEnter = mainLineUpdate.enter().append('g').attr('class', classChartLine).style('opacity', 0).style("pointer-events", "none"); + // Lines for each data + mainLineEnter.append('g').attr("class", classLines); + // Areas + mainLineEnter.append('g').attr('class', classAreas); + // Circles for each data point on lines + mainLineEnter.append('g').attr("class", function (d) { + return $$.generateClass(CLASS.selectedCircles, d.id); + }); + mainLineEnter.append('g').attr("class", classCircles).style("cursor", function (d) { + return config.data_selection_isselectable(d) ? "pointer" : null; + }); + // Update date for selected circles + targets.forEach(function (t) { + $$.main.selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)).selectAll('.' + CLASS.selectedCircle).each(function (d) { + d.value = t.values[d.index].value; + }); + }); + // MEMO: can not keep same color... + //mainLineUpdate.exit().remove(); +}; +c3_chart_internal_fn.updateLine = function (durationForExit) { + var $$ = this; + $$.mainLine = $$.main.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line).data($$.lineData.bind($$)); + $$.mainLine.enter().append('path').attr('class', $$.classLine.bind($$)).style("stroke", $$.color); + $$.mainLine.style("opacity", $$.initialOpacity.bind($$)).style('shape-rendering', function (d) { + return $$.isStepType(d) ? 'crispEdges' : ''; + }).attr('transform', null); + $$.mainLine.exit().transition().duration(durationForExit).style('opacity', 0).remove(); +}; +c3_chart_internal_fn.redrawLine = function (drawLine, withTransition) { + return [(withTransition ? this.mainLine.transition(Math.random().toString()) : this.mainLine).attr("d", drawLine).style("stroke", this.color).style("opacity", 1)]; +}; +c3_chart_internal_fn.generateDrawLine = function (lineIndices, isSub) { + var $$ = this, + config = $$.config, + line = $$.d3.svg.line(), + getPoints = $$.generateGetLinePoints(lineIndices, isSub), + yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, + xValue = function xValue(d) { + return (isSub ? $$.subxx : $$.xx).call($$, d); + }, + yValue = function yValue(d, i) { + return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value); + }; + + line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue); + if (!config.line_connectNull) { + line = line.defined(function (d) { + return d.value != null; + }); + } + return function (d) { + var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, + x = isSub ? $$.x : $$.subX, + y = yScaleGetter.call($$, d.id), + x0 = 0, + y0 = 0, + path; + if ($$.isLineType(d)) { + if (config.data_regions[d.id]) { + path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]); + } else { + if ($$.isStepType(d)) { + values = $$.convertValuesToStep(values); + } + path = line.interpolate($$.getInterpolate(d))(values); + } + } else { + if (values[0]) { + x0 = x(values[0].x); + y0 = y(values[0].value); + } + path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0; + } + return path ? path : "M 0 0"; + }; +}; +c3_chart_internal_fn.generateGetLinePoints = function (lineIndices, isSub) { + // partial duplication of generateGetBarPoints + var $$ = this, + config = $$.config, + lineTargetsNum = lineIndices.__max__ + 1, + x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub), + y = $$.getShapeY(!!isSub), + lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub), + yScale = isSub ? $$.getSubYScale : $$.getYScale; + return function (d, i) { + var y0 = yScale.call($$, d.id)(0), + offset = lineOffset(d, i) || y0, + // offset is for stacked area chart + posX = x(d), + posY = y(d); + // fix posY not to overflow opposite quadrant + if (config.axis_rotated) { + if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) { + posY = y0; + } + } + // 1 point that marks the line position + return [[posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility + [posX, posY - (y0 - offset)], // needed for compatibility + [posX, posY - (y0 - offset)] // needed for compatibility + ]; + }; +}; + +c3_chart_internal_fn.lineWithRegions = function (d, x, y, _regions) { + var $$ = this, + config = $$.config, + prev = -1, + i, + j, + s = "M", + sWithRegion, + xp, + yp, + dx, + dy, + dd, + diff, + diffx2, + xOffset = $$.isCategorized() ? 0.5 : 0, + xValue, + yValue, + regions = []; + + function isWithinRegions(x, regions) { + var i; + for (i = 0; i < regions.length; i++) { + if (regions[i].start < x && x <= regions[i].end) { + return true; + } + } + return false; + } + + // Check start/end of regions + if (isDefined(_regions)) { + for (i = 0; i < _regions.length; i++) { + regions[i] = {}; + if (isUndefined(_regions[i].start)) { + regions[i].start = d[0].x; + } else { + regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start; + } + if (isUndefined(_regions[i].end)) { + regions[i].end = d[d.length - 1].x; + } else { + regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end; + } + } + } + + // Set scales + xValue = config.axis_rotated ? function (d) { + return y(d.value); + } : function (d) { + return x(d.x); + }; + yValue = config.axis_rotated ? function (d) { + return x(d.x); + } : function (d) { + return y(d.value); + }; + + // Define svg generator function for region + function generateM(points) { + return 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1]; + } + if ($$.isTimeSeries()) { + sWithRegion = function sWithRegion(d0, d1, j, diff) { + var x0 = d0.x.getTime(), + x_diff = d1.x - d0.x, + xv0 = new Date(x0 + x_diff * j), + xv1 = new Date(x0 + x_diff * (j + diff)), + points; + if (config.axis_rotated) { + points = [[y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)]]; + } else { + points = [[x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))]]; + } + return generateM(points); + }; + } else { + sWithRegion = function sWithRegion(d0, d1, j, diff) { + var points; + if (config.axis_rotated) { + points = [[y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))]]; + } else { + points = [[x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))]]; + } + return generateM(points); + }; + } + + // Generate + for (i = 0; i < d.length; i++) { + + // Draw as normal + if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) { + s += " " + xValue(d[i]) + " " + yValue(d[i]); + } + // Draw with region // TODO: Fix for horizotal charts + else { + xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries()); + yp = $$.getScale(d[i - 1].value, d[i].value); + + dx = x(d[i].x) - x(d[i - 1].x); + dy = y(d[i].value) - y(d[i - 1].value); + dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); + diff = 2 / dd; + diffx2 = diff * 2; + + for (j = diff; j <= 1; j += diffx2) { + s += sWithRegion(d[i - 1], d[i], j, diff); + } + } + prev = d[i].x; + } + + return s; +}; + +c3_chart_internal_fn.updateArea = function (durationForExit) { + var $$ = this, + d3 = $$.d3; + $$.mainArea = $$.main.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area).data($$.lineData.bind($$)); + $$.mainArea.enter().append('path').attr("class", $$.classArea.bind($$)).style("fill", $$.color).style("opacity", function () { + $$.orgAreaOpacity = +d3.select(this).style('opacity');return 0; + }); + $$.mainArea.style("opacity", $$.orgAreaOpacity); + $$.mainArea.exit().transition().duration(durationForExit).style('opacity', 0).remove(); +}; +c3_chart_internal_fn.redrawArea = function (drawArea, withTransition) { + return [(withTransition ? this.mainArea.transition(Math.random().toString()) : this.mainArea).attr("d", drawArea).style("fill", this.color).style("opacity", this.orgAreaOpacity)]; +}; +c3_chart_internal_fn.generateDrawArea = function (areaIndices, isSub) { + var $$ = this, + config = $$.config, + area = $$.d3.svg.area(), + getPoints = $$.generateGetAreaPoints(areaIndices, isSub), + yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, + xValue = function xValue(d) { + return (isSub ? $$.subxx : $$.xx).call($$, d); + }, + value0 = function value0(d, i) { + return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id)); + }, + value1 = function value1(d, i) { + return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value); + }; + + area = config.axis_rotated ? area.x0(value0).x1(value1).y(xValue) : area.x(xValue).y0(config.area_above ? 0 : value0).y1(value1); + if (!config.line_connectNull) { + area = area.defined(function (d) { + return d.value !== null; + }); + } + + return function (d) { + var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, + x0 = 0, + y0 = 0, + path; + if ($$.isAreaType(d)) { + if ($$.isStepType(d)) { + values = $$.convertValuesToStep(values); + } + path = area.interpolate($$.getInterpolate(d))(values); + } else { + if (values[0]) { + x0 = $$.x(values[0].x); + y0 = $$.getYScale(d.id)(values[0].value); + } + path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0; + } + return path ? path : "M 0 0"; + }; +}; +c3_chart_internal_fn.getAreaBaseValue = function () { + return 0; +}; +c3_chart_internal_fn.generateGetAreaPoints = function (areaIndices, isSub) { + // partial duplication of generateGetBarPoints + var $$ = this, + config = $$.config, + areaTargetsNum = areaIndices.__max__ + 1, + x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub), + y = $$.getShapeY(!!isSub), + areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub), + yScale = isSub ? $$.getSubYScale : $$.getYScale; + return function (d, i) { + var y0 = yScale.call($$, d.id)(0), + offset = areaOffset(d, i) || y0, + // offset is for stacked area chart + posX = x(d), + posY = y(d); + // fix posY not to overflow opposite quadrant + if (config.axis_rotated) { + if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) { + posY = y0; + } + } + // 1 point that marks the area position + return [[posX, offset], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility + [posX, offset] // needed for compatibility + ]; + }; +}; + +c3_chart_internal_fn.updateCircle = function () { + var $$ = this; + $$.mainCircle = $$.main.selectAll('.' + CLASS.circles).selectAll('.' + CLASS.circle).data($$.lineOrScatterData.bind($$)); + $$.mainCircle.enter().append("circle").attr("class", $$.classCircle.bind($$)).attr("r", $$.pointR.bind($$)).style("fill", $$.color); + $$.mainCircle.style("opacity", $$.initialOpacityForCircle.bind($$)); + $$.mainCircle.exit().remove(); +}; +c3_chart_internal_fn.redrawCircle = function (cx, cy, withTransition) { + var selectedCircles = this.main.selectAll('.' + CLASS.selectedCircle); + return [(withTransition ? this.mainCircle.transition(Math.random().toString()) : this.mainCircle).style('opacity', this.opacityForCircle.bind(this)).style("fill", this.color).attr("cx", cx).attr("cy", cy), (withTransition ? selectedCircles.transition(Math.random().toString()) : selectedCircles).attr("cx", cx).attr("cy", cy)]; +}; +c3_chart_internal_fn.circleX = function (d) { + return d.x || d.x === 0 ? this.x(d.x) : null; +}; +c3_chart_internal_fn.updateCircleY = function () { + var $$ = this, + lineIndices, + getPoints; + if ($$.config.data_groups.length > 0) { + lineIndices = $$.getShapeIndices($$.isLineType), getPoints = $$.generateGetLinePoints(lineIndices); + $$.circleY = function (d, i) { + return getPoints(d, i)[0][1]; + }; + } else { + $$.circleY = function (d) { + return $$.getYScale(d.id)(d.value); + }; + } +}; +c3_chart_internal_fn.getCircles = function (i, id) { + var $$ = this; + return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : '')); +}; +c3_chart_internal_fn.expandCircles = function (i, id, reset) { + var $$ = this, + r = $$.pointExpandedR.bind($$); + if (reset) { + $$.unexpandCircles(); + } + $$.getCircles(i, id).classed(CLASS.EXPANDED, true).attr('r', r); +}; +c3_chart_internal_fn.unexpandCircles = function (i) { + var $$ = this, + r = $$.pointR.bind($$); + $$.getCircles(i).filter(function () { + return $$.d3.select(this).classed(CLASS.EXPANDED); + }).classed(CLASS.EXPANDED, false).attr('r', r); +}; +c3_chart_internal_fn.pointR = function (d) { + var $$ = this, + config = $$.config; + return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r; +}; +c3_chart_internal_fn.pointExpandedR = function (d) { + var $$ = this, + config = $$.config; + if (config.point_focus_expand_enabled) { + return isFunction(config.point_focus_expand_r) ? config.point_focus_expand_r(d) : config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75; + } else { + return $$.pointR(d); + } +}; +c3_chart_internal_fn.pointSelectR = function (d) { + var $$ = this, + config = $$.config; + return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4; +}; +c3_chart_internal_fn.isWithinCircle = function (that, r) { + var d3 = this.d3, + mouse = d3.mouse(that), + d3_this = d3.select(that), + cx = +d3_this.attr("cx"), + cy = +d3_this.attr("cy"); + return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r; +}; +c3_chart_internal_fn.isWithinStep = function (that, y) { + return Math.abs(y - this.d3.mouse(that)[1]) < 30; +}; + +c3_chart_internal_fn.getCurrentWidth = function () { + var $$ = this, + config = $$.config; + return config.size_width ? config.size_width : $$.getParentWidth(); +}; +c3_chart_internal_fn.getCurrentHeight = function () { + var $$ = this, + config = $$.config, + h = config.size_height ? config.size_height : $$.getParentHeight(); + return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1); +}; +c3_chart_internal_fn.getCurrentPaddingTop = function () { + var $$ = this, + config = $$.config, + padding = isValue(config.padding_top) ? config.padding_top : 0; + if ($$.title && $$.title.node()) { + padding += $$.getTitlePadding(); + } + return padding; +}; +c3_chart_internal_fn.getCurrentPaddingBottom = function () { + var config = this.config; + return isValue(config.padding_bottom) ? config.padding_bottom : 0; +}; +c3_chart_internal_fn.getCurrentPaddingLeft = function (withoutRecompute) { + var $$ = this, + config = $$.config; + if (isValue(config.padding_left)) { + return config.padding_left; + } else if (config.axis_rotated) { + return !config.axis_x_show ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40); + } else if (!config.axis_y_show || config.axis_y_inner) { + // && !config.axis_rotated + return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1; + } else { + return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute)); + } +}; +c3_chart_internal_fn.getCurrentPaddingRight = function () { + var $$ = this, + config = $$.config, + defaultPadding = 10, + legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0; + if (isValue(config.padding_right)) { + return config.padding_right + 1; // 1 is needed not to hide tick line + } else if (config.axis_rotated) { + return defaultPadding + legendWidthOnRight; + } else if (!config.axis_y2_show || config.axis_y2_inner) { + // && !config.axis_rotated + return 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0); + } else { + return ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight; + } +}; + +c3_chart_internal_fn.getParentRectValue = function (key) { + var parent = this.selectChart.node(), + v; + while (parent && parent.tagName !== 'BODY') { + try { + v = parent.getBoundingClientRect()[key]; + } catch (e) { + if (key === 'width') { + // In IE in certain cases getBoundingClientRect + // will cause an "unspecified error" + v = parent.offsetWidth; + } + } + if (v) { + break; + } + parent = parent.parentNode; + } + return v; +}; +c3_chart_internal_fn.getParentWidth = function () { + return this.getParentRectValue('width'); +}; +c3_chart_internal_fn.getParentHeight = function () { + var h = this.selectChart.style('height'); + return h.indexOf('px') > 0 ? +h.replace('px', '') : 0; +}; + +c3_chart_internal_fn.getSvgLeft = function (withoutRecompute) { + var $$ = this, + config = $$.config, + hasLeftAxisRect = config.axis_rotated || !config.axis_rotated && !config.axis_y_inner, + leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY, + leftAxis = $$.main.select('.' + leftAxisClass).node(), + svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : { right: 0 }, + chartRect = $$.selectChart.node().getBoundingClientRect(), + hasArc = $$.hasArcType(), + svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute)); + return svgLeft > 0 ? svgLeft : 0; +}; + +c3_chart_internal_fn.getAxisWidthByAxisId = function (id, withoutRecompute) { + var $$ = this, + position = $$.axis.getLabelPositionById(id); + return $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40); +}; +c3_chart_internal_fn.getHorizontalAxisHeight = function (axisId) { + var $$ = this, + config = $$.config, + h = 30; + if (axisId === 'x' && !config.axis_x_show) { + return 8; + } + if (axisId === 'x' && config.axis_x_height) { + return config.axis_x_height; + } + if (axisId === 'y' && !config.axis_y_show) { + return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1; + } + if (axisId === 'y2' && !config.axis_y2_show) { + return $$.rotated_padding_top; + } + // Calculate x axis height when tick rotated + if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) { + h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - config.axis_x_tick_rotate) / 180); + } + // Calculate y axis height when tick rotated + if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) { + h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - config.axis_y_tick_rotate) / 180); + } + return h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0); +}; + +c3_chart_internal_fn.getEventRectWidth = function () { + return Math.max(0, this.xAxis.tickInterval()); +}; + +c3_chart_internal_fn.initBrush = function () { + var $$ = this, + d3 = $$.d3; + $$.brush = d3.svg.brush().on("brush", function () { + $$.redrawForBrush(); + }); + $$.brush.update = function () { + if ($$.context) { + $$.context.select('.' + CLASS.brush).call(this); + } + return this; + }; + $$.brush.scale = function (scale) { + return $$.config.axis_rotated ? this.y(scale) : this.x(scale); + }; +}; +c3_chart_internal_fn.initSubchart = function () { + var $$ = this, + config = $$.config, + context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')), + visibility = config.subchart_show ? 'visible' : 'hidden'; + + context.style('visibility', visibility); + + // Define g for chart area + context.append('g').attr("clip-path", $$.clipPathForSubchart).attr('class', CLASS.chart); + + // Define g for bar chart area + context.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartBars); + + // Define g for line chart area + context.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartLines); + + // Add extent rect for Brush + context.append("g").attr("clip-path", $$.clipPath).attr("class", CLASS.brush).call($$.brush); + + // ATTENTION: This must be called AFTER chart added + // Add Axis + $$.axes.subx = context.append("g").attr("class", CLASS.axisX).attr("transform", $$.getTranslate('subx')).attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis).style("visibility", config.subchart_axis_x_show ? visibility : 'hidden'); +}; +c3_chart_internal_fn.updateTargetsForSubchart = function (targets) { + var $$ = this, + context = $$.context, + config = $$.config, + contextLineEnter, + contextLineUpdate, + contextBarEnter, + contextBarUpdate, + classChartBar = $$.classChartBar.bind($$), + classBars = $$.classBars.bind($$), + classChartLine = $$.classChartLine.bind($$), + classLines = $$.classLines.bind($$), + classAreas = $$.classAreas.bind($$); + + if (config.subchart_show) { + //-- Bar --// + contextBarUpdate = context.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar).data(targets).attr('class', classChartBar); + contextBarEnter = contextBarUpdate.enter().append('g').style('opacity', 0).attr('class', classChartBar); + // Bars for each data + contextBarEnter.append('g').attr("class", classBars); + + //-- Line --// + contextLineUpdate = context.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine).data(targets).attr('class', classChartLine); + contextLineEnter = contextLineUpdate.enter().append('g').style('opacity', 0).attr('class', classChartLine); + // Lines for each data + contextLineEnter.append("g").attr("class", classLines); + // Area + contextLineEnter.append("g").attr("class", classAreas); + + //-- Brush --// + context.selectAll('.' + CLASS.brush + ' rect').attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2); + } +}; +c3_chart_internal_fn.updateBarForSubchart = function (durationForExit) { + var $$ = this; + $$.contextBar = $$.context.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar).data($$.barData.bind($$)); + $$.contextBar.enter().append('path').attr("class", $$.classBar.bind($$)).style("stroke", 'none').style("fill", $$.color); + $$.contextBar.style("opacity", $$.initialOpacity.bind($$)); + $$.contextBar.exit().transition().duration(durationForExit).style('opacity', 0).remove(); +}; +c3_chart_internal_fn.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) { + (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar).attr('d', drawBarOnSub).style('opacity', 1); +}; +c3_chart_internal_fn.updateLineForSubchart = function (durationForExit) { + var $$ = this; + $$.contextLine = $$.context.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line).data($$.lineData.bind($$)); + $$.contextLine.enter().append('path').attr('class', $$.classLine.bind($$)).style('stroke', $$.color); + $$.contextLine.style("opacity", $$.initialOpacity.bind($$)); + $$.contextLine.exit().transition().duration(durationForExit).style('opacity', 0).remove(); +}; +c3_chart_internal_fn.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) { + (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine).attr("d", drawLineOnSub).style('opacity', 1); +}; +c3_chart_internal_fn.updateAreaForSubchart = function (durationForExit) { + var $$ = this, + d3 = $$.d3; + $$.contextArea = $$.context.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area).data($$.lineData.bind($$)); + $$.contextArea.enter().append('path').attr("class", $$.classArea.bind($$)).style("fill", $$.color).style("opacity", function () { + $$.orgAreaOpacity = +d3.select(this).style('opacity');return 0; + }); + $$.contextArea.style("opacity", 0); + $$.contextArea.exit().transition().duration(durationForExit).style('opacity', 0).remove(); +}; +c3_chart_internal_fn.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) { + (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea).attr("d", drawAreaOnSub).style("fill", this.color).style("opacity", this.orgAreaOpacity); +}; +c3_chart_internal_fn.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) { + var $$ = this, + d3 = $$.d3, + config = $$.config, + drawAreaOnSub, + drawBarOnSub, + drawLineOnSub; + + $$.context.style('visibility', config.subchart_show ? 'visible' : 'hidden'); + + // subchart + if (config.subchart_show) { + // reflect main chart to extent on subchart if zoomed + if (d3.event && d3.event.type === 'zoom') { + $$.brush.extent($$.x.orgDomain()).update(); + } + // update subchart elements if needed + if (withSubchart) { + + // extent rect + if (!$$.brush.empty()) { + $$.brush.extent($$.x.orgDomain()).update(); + } + // setup drawer - MEMO: this must be called after axis updated + drawAreaOnSub = $$.generateDrawArea(areaIndices, true); + drawBarOnSub = $$.generateDrawBar(barIndices, true); + drawLineOnSub = $$.generateDrawLine(lineIndices, true); + + $$.updateBarForSubchart(duration); + $$.updateLineForSubchart(duration); + $$.updateAreaForSubchart(duration); + + $$.redrawBarForSubchart(drawBarOnSub, duration, duration); + $$.redrawLineForSubchart(drawLineOnSub, duration, duration); + $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration); + } + } +}; +c3_chart_internal_fn.redrawForBrush = function () { + var $$ = this, + x = $$.x; + $$.redraw({ + withTransition: false, + withY: $$.config.zoom_rescale, + withSubchart: false, + withUpdateXDomain: true, + withDimension: false + }); + $$.config.subchart_onbrush.call($$.api, x.orgDomain()); +}; +c3_chart_internal_fn.transformContext = function (withTransition, transitions) { + var $$ = this, + subXAxis; + if (transitions && transitions.axisSubX) { + subXAxis = transitions.axisSubX; + } else { + subXAxis = $$.context.select('.' + CLASS.axisX); + if (withTransition) { + subXAxis = subXAxis.transition(); + } + } + $$.context.attr("transform", $$.getTranslate('context')); + subXAxis.attr("transform", $$.getTranslate('subx')); +}; +c3_chart_internal_fn.getDefaultExtent = function () { + var $$ = this, + config = $$.config, + extent = isFunction(config.axis_x_extent) ? config.axis_x_extent($$.getXDomain($$.data.targets)) : config.axis_x_extent; + if ($$.isTimeSeries()) { + extent = [$$.parseDate(extent[0]), $$.parseDate(extent[1])]; + } + return extent; +}; + +c3_chart_internal_fn.initText = function () { + var $$ = this; + $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartTexts); + $$.mainText = $$.d3.selectAll([]); +}; +c3_chart_internal_fn.updateTargetsForText = function (targets) { + var $$ = this, + mainTextUpdate, + mainTextEnter, + classChartText = $$.classChartText.bind($$), + classTexts = $$.classTexts.bind($$), + classFocus = $$.classFocus.bind($$); + mainTextUpdate = $$.main.select('.' + CLASS.chartTexts).selectAll('.' + CLASS.chartText).data(targets).attr('class', function (d) { + return classChartText(d) + classFocus(d); + }); + mainTextEnter = mainTextUpdate.enter().append('g').attr('class', classChartText).style('opacity', 0).style("pointer-events", "none"); + mainTextEnter.append('g').attr('class', classTexts); +}; +c3_chart_internal_fn.updateText = function (durationForExit) { + var $$ = this, + config = $$.config, + barOrLineData = $$.barOrLineData.bind($$), + classText = $$.classText.bind($$); + $$.mainText = $$.main.selectAll('.' + CLASS.texts).selectAll('.' + CLASS.text).data(barOrLineData); + $$.mainText.enter().append('text').attr("class", classText).attr('text-anchor', function (d) { + return config.axis_rotated ? d.value < 0 ? 'end' : 'start' : 'middle'; + }).style("stroke", 'none').style("fill", function (d) { + return $$.color(d); + }).style("fill-opacity", 0); + $$.mainText.text(function (d, i, j) { + return $$.dataLabelFormat(d.id)(d.value, d.id, i, j); + }); + $$.mainText.exit().transition().duration(durationForExit).style('fill-opacity', 0).remove(); +}; +c3_chart_internal_fn.redrawText = function (xForText, yForText, forFlow, withTransition) { + return [(withTransition ? this.mainText.transition() : this.mainText).attr('x', xForText).attr('y', yForText).style("fill", this.color).style("fill-opacity", forFlow ? 0 : this.opacityForText.bind(this))]; +}; +c3_chart_internal_fn.getTextRect = function (text, cls, element) { + var dummy = this.d3.select('body').append('div').classed('c3', true), + svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0), + font = this.d3.select(element).style('font'), + rect; + svg.selectAll('.dummy').data([text]).enter().append('text').classed(cls ? cls : "", true).style('font', font).text(text).each(function () { + rect = this.getBoundingClientRect(); + }); + dummy.remove(); + return rect; +}; +c3_chart_internal_fn.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) { + var $$ = this, + getAreaPoints = $$.generateGetAreaPoints(areaIndices, false), + getBarPoints = $$.generateGetBarPoints(barIndices, false), + getLinePoints = $$.generateGetLinePoints(lineIndices, false), + getter = forX ? $$.getXForText : $$.getYForText; + return function (d, i) { + var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints; + return getter.call($$, getPoints(d, i), d, this); + }; +}; +c3_chart_internal_fn.getXForText = function (points, d, textElement) { + var $$ = this, + box = textElement.getBoundingClientRect(), + xPos, + padding; + if ($$.config.axis_rotated) { + padding = $$.isBarType(d) ? 4 : 6; + xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1); + } else { + xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0]; + } + // show labels regardless of the domain if value is null + if (d.value === null) { + if (xPos > $$.width) { + xPos = $$.width - box.width; + } else if (xPos < 0) { + xPos = 4; + } + } + return xPos; +}; +c3_chart_internal_fn.getYForText = function (points, d, textElement) { + var $$ = this, + box = textElement.getBoundingClientRect(), + yPos; + if ($$.config.axis_rotated) { + yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2; + } else { + yPos = points[2][1]; + if (d.value < 0 || d.value === 0 && !$$.hasPositiveValue) { + yPos += box.height; + if ($$.isBarType(d) && $$.isSafari()) { + yPos -= 3; + } else if (!$$.isBarType(d) && $$.isChrome()) { + yPos += 3; + } + } else { + yPos += $$.isBarType(d) ? -3 : -6; + } + } + // show labels regardless of the domain if value is null + if (d.value === null && !$$.config.axis_rotated) { + if (yPos < box.height) { + yPos = box.height; + } else if (yPos > this.height) { + yPos = this.height - 4; + } + } + return yPos; +}; + +c3_chart_internal_fn.initTitle = function () { + var $$ = this; + $$.title = $$.svg.append("text").text($$.config.title_text).attr("class", $$.CLASS.title); +}; +c3_chart_internal_fn.redrawTitle = function () { + var $$ = this; + $$.title.attr("x", $$.xForTitle.bind($$)).attr("y", $$.yForTitle.bind($$)); +}; +c3_chart_internal_fn.xForTitle = function () { + var $$ = this, + config = $$.config, + position = config.title_position || 'left', + x; + if (position.indexOf('right') >= 0) { + x = $$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width - config.title_padding.right; + } else if (position.indexOf('center') >= 0) { + x = ($$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width) / 2; + } else { + // left + x = config.title_padding.left; + } + return x; +}; +c3_chart_internal_fn.yForTitle = function () { + var $$ = this; + return $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).height; +}; +c3_chart_internal_fn.getTitlePadding = function () { + var $$ = this; + return $$.yForTitle() + $$.config.title_padding.bottom; +}; + +c3_chart_internal_fn.initTooltip = function () { + var $$ = this, + config = $$.config, + i; + $$.tooltip = $$.selectChart.style("position", "relative").append("div").attr('class', CLASS.tooltipContainer).style("position", "absolute").style("pointer-events", "none").style("display", "none"); + // Show tooltip if needed + if (config.tooltip_init_show) { + if ($$.isTimeSeries() && isString(config.tooltip_init_x)) { + config.tooltip_init_x = $$.parseDate(config.tooltip_init_x); + for (i = 0; i < $$.data.targets[0].values.length; i++) { + if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) { + break; + } + } + config.tooltip_init_x = i; + } + $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) { + return $$.addName(d.values[config.tooltip_init_x]); + }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color)); + $$.tooltip.style("top", config.tooltip_init_position.top).style("left", config.tooltip_init_position.left).style("display", "block"); + } +}; +c3_chart_internal_fn.getTooltipSortFunction = function () { + var $$ = this, + config = $$.config; + + if (config.data_groups.length === 0 || config.tooltip_order !== undefined) { + // if data are not grouped or if an order is specified + // for the tooltip values we sort them by their values + + var order = config.tooltip_order; + if (order === undefined) { + order = config.data_order; + } + + var valueOf = function valueOf(obj) { + return obj ? obj.value : null; + }; + + // if data are not grouped, we sort them by their value + if (isString(order) && order.toLowerCase() === 'asc') { + return function (a, b) { + return valueOf(a) - valueOf(b); + }; + } else if (isString(order) && order.toLowerCase() === 'desc') { + return function (a, b) { + return valueOf(b) - valueOf(a); + }; + } else if (isFunction(order)) { + + // if the function is from data_order we need + // to wrap the returned function in order to format + // the sorted value to the expected format + + var sortFunction = order; + + if (config.tooltip_order === undefined) { + sortFunction = function sortFunction(a, b) { + return order(a ? { + id: a.id, + values: [a] + } : null, b ? { + id: b.id, + values: [b] + } : null); + }; + } + + return sortFunction; + } else if (isArray(order)) { + return function (a, b) { + return order.indexOf(a.id) - order.indexOf(b.id); + }; + } + } else { + // if data are grouped, we follow the order of grouped targets + var ids = $$.orderTargets($$.data.targets).map(function (i) { + return i.id; + }); + + // if it was either asc or desc we need to invert the order + // returned by orderTargets + if ($$.isOrderAsc() || $$.isOrderDesc()) { + ids = ids.reverse(); + } + + return function (a, b) { + return ids.indexOf(a.id) - ids.indexOf(b.id); + }; + } +}; +c3_chart_internal_fn.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) { + var $$ = this, + config = $$.config, + titleFormat = config.tooltip_format_title || defaultTitleFormat, + nameFormat = config.tooltip_format_name || function (name) { + return name; + }, + valueFormat = config.tooltip_format_value || defaultValueFormat, + text, + i, + title, + value, + name, + bgcolor; + + var tooltipSortFunction = this.getTooltipSortFunction(); + if (tooltipSortFunction) { + d.sort(tooltipSortFunction); + } + + for (i = 0; i < d.length; i++) { + if (!(d[i] && (d[i].value || d[i].value === 0))) { + continue; + } + + if (!text) { + title = sanitise(titleFormat ? titleFormat(d[i].x) : d[i].x); + text = "" + (title || title === 0 ? "" : ""); + } + + value = sanitise(valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d)); + if (value !== undefined) { + // Skip elements when their name is set to null + if (d[i].name === null) { + continue; + } + name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)); + bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); + + text += ""; + text += ""; + text += ""; + text += ""; + } + } + return text + "
" + title + "
" + name + "" + value + "
"; +}; +c3_chart_internal_fn.tooltipPosition = function (dataToShow, tWidth, tHeight, element) { + var $$ = this, + config = $$.config, + d3 = $$.d3; + var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight; + var forArc = $$.hasArcType(), + mouse = d3.mouse(element); + // Determin tooltip position + if (forArc) { + tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0]; + tooltipTop = $$.height / 2 + mouse[1] + 20; + } else { + svgLeft = $$.getSvgLeft(true); + if (config.axis_rotated) { + tooltipLeft = svgLeft + mouse[0] + 100; + tooltipRight = tooltipLeft + tWidth; + chartRight = $$.currentWidth - $$.getCurrentPaddingRight(); + tooltipTop = $$.x(dataToShow[0].x) + 20; + } else { + tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20; + tooltipRight = tooltipLeft + tWidth; + chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight(); + tooltipTop = mouse[1] + 15; + } + + if (tooltipRight > chartRight) { + // 20 is needed for Firefox to keep tooltip width + tooltipLeft -= tooltipRight - chartRight + 20; + } + if (tooltipTop + tHeight > $$.currentHeight) { + tooltipTop -= tHeight + 30; + } + } + if (tooltipTop < 0) { + tooltipTop = 0; + } + return { top: tooltipTop, left: tooltipLeft }; +}; +c3_chart_internal_fn.showTooltip = function (selectedData, element) { + var $$ = this, + config = $$.config; + var tWidth, tHeight, position; + var forArc = $$.hasArcType(), + dataToShow = selectedData.filter(function (d) { + return d && isValue(d.value); + }), + positionFunction = config.tooltip_position || c3_chart_internal_fn.tooltipPosition; + if (dataToShow.length === 0 || !config.tooltip_show) { + return; + } + $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block"); + + // Get tooltip dimensions + tWidth = $$.tooltip.property('offsetWidth'); + tHeight = $$.tooltip.property('offsetHeight'); + + position = positionFunction.call(this, dataToShow, tWidth, tHeight, element); + // Set tooltip + $$.tooltip.style("top", position.top + "px").style("left", position.left + 'px'); +}; +c3_chart_internal_fn.hideTooltip = function () { + this.tooltip.style("display", "none"); +}; + +c3_chart_internal_fn.setTargetType = function (targetIds, type) { + var $$ = this, + config = $$.config; + $$.mapToTargetIds(targetIds).forEach(function (id) { + $$.withoutFadeIn[id] = type === config.data_types[id]; + config.data_types[id] = type; + }); + if (!targetIds) { + config.data_type = type; + } +}; +c3_chart_internal_fn.hasType = function (type, targets) { + var $$ = this, + types = $$.config.data_types, + has = false; + targets = targets || $$.data.targets; + if (targets && targets.length) { + targets.forEach(function (target) { + var t = types[target.id]; + if (t && t.indexOf(type) >= 0 || !t && type === 'line') { + has = true; + } + }); + } else if (Object.keys(types).length) { + Object.keys(types).forEach(function (id) { + if (types[id] === type) { + has = true; + } + }); + } else { + has = $$.config.data_type === type; + } + return has; +}; +c3_chart_internal_fn.hasArcType = function (targets) { + return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets); +}; +c3_chart_internal_fn.isLineType = function (d) { + var config = this.config, + id = isString(d) ? d : d.id; + return !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0; +}; +c3_chart_internal_fn.isStepType = function (d) { + var id = isString(d) ? d : d.id; + return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0; +}; +c3_chart_internal_fn.isSplineType = function (d) { + var id = isString(d) ? d : d.id; + return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0; +}; +c3_chart_internal_fn.isAreaType = function (d) { + var id = isString(d) ? d : d.id; + return ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0; +}; +c3_chart_internal_fn.isBarType = function (d) { + var id = isString(d) ? d : d.id; + return this.config.data_types[id] === 'bar'; +}; +c3_chart_internal_fn.isScatterType = function (d) { + var id = isString(d) ? d : d.id; + return this.config.data_types[id] === 'scatter'; +}; +c3_chart_internal_fn.isPieType = function (d) { + var id = isString(d) ? d : d.id; + return this.config.data_types[id] === 'pie'; +}; +c3_chart_internal_fn.isGaugeType = function (d) { + var id = isString(d) ? d : d.id; + return this.config.data_types[id] === 'gauge'; +}; +c3_chart_internal_fn.isDonutType = function (d) { + var id = isString(d) ? d : d.id; + return this.config.data_types[id] === 'donut'; +}; +c3_chart_internal_fn.isArcType = function (d) { + return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d); +}; +c3_chart_internal_fn.lineData = function (d) { + return this.isLineType(d) ? [d] : []; +}; +c3_chart_internal_fn.arcData = function (d) { + return this.isArcType(d.data) ? [d] : []; +}; +/* not used + function scatterData(d) { + return isScatterType(d) ? d.values : []; + } + */ +c3_chart_internal_fn.barData = function (d) { + return this.isBarType(d) ? d.values : []; +}; +c3_chart_internal_fn.lineOrScatterData = function (d) { + return this.isLineType(d) || this.isScatterType(d) ? d.values : []; +}; +c3_chart_internal_fn.barOrLineData = function (d) { + return this.isBarType(d) || this.isLineType(d) ? d.values : []; +}; +c3_chart_internal_fn.isInterpolationType = function (type) { + return ['linear', 'linear-closed', 'basis', 'basis-open', 'basis-closed', 'bundle', 'cardinal', 'cardinal-open', 'cardinal-closed', 'monotone'].indexOf(type) >= 0; +}; + +c3_chart_internal_fn.isSafari = function () { + var ua = window.navigator.userAgent; + return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0; +}; +c3_chart_internal_fn.isChrome = function () { + var ua = window.navigator.userAgent; + return ua.indexOf('Chrome') >= 0; +}; + +c3_chart_internal_fn.initZoom = function () { + var $$ = this, + d3 = $$.d3, + config = $$.config, + startEvent; + + $$.zoom = d3.behavior.zoom().on("zoomstart", function () { + startEvent = d3.event.sourceEvent; + $$.zoom.altDomain = d3.event.sourceEvent.altKey ? $$.x.orgDomain() : null; + config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent); + }).on("zoom", function () { + $$.redrawForZoom.call($$); + }).on('zoomend', function () { + var event = d3.event.sourceEvent; + // if click, do nothing. otherwise, click interaction will be canceled. + if (event && startEvent.clientX === event.clientX && startEvent.clientY === event.clientY) { + return; + } + $$.redrawEventRect(); + $$.updateZoom(); + config.zoom_onzoomend.call($$.api, $$.x.orgDomain()); + }); + $$.zoom.scale = function (scale) { + return config.axis_rotated ? this.y(scale) : this.x(scale); + }; + $$.zoom.orgScaleExtent = function () { + var extent = config.zoom_extent ? config.zoom_extent : [1, 10]; + return [extent[0], Math.max($$.getMaxDataCount() / extent[1], extent[1])]; + }; + $$.zoom.updateScaleExtent = function () { + var ratio = diffDomain($$.x.orgDomain()) / diffDomain($$.getZoomDomain()), + extent = this.orgScaleExtent(); + this.scaleExtent([extent[0] * ratio, extent[1] * ratio]); + return this; + }; +}; +c3_chart_internal_fn.getZoomDomain = function () { + var $$ = this, + config = $$.config, + d3 = $$.d3, + min = d3.min([$$.orgXDomain[0], config.zoom_x_min]), + max = d3.max([$$.orgXDomain[1], config.zoom_x_max]); + return [min, max]; +}; +c3_chart_internal_fn.updateZoom = function () { + var $$ = this, + z = $$.config.zoom_enabled ? $$.zoom : function () {}; + $$.main.select('.' + CLASS.zoomRect).call(z).on("dblclick.zoom", null); + $$.main.selectAll('.' + CLASS.eventRect).call(z).on("dblclick.zoom", null); +}; +c3_chart_internal_fn.redrawForZoom = function () { + var $$ = this, + d3 = $$.d3, + config = $$.config, + zoom = $$.zoom, + x = $$.x; + if (!config.zoom_enabled) { + return; + } + if ($$.filterTargetsToShow($$.data.targets).length === 0) { + return; + } + if (d3.event.sourceEvent.type === 'mousemove' && zoom.altDomain) { + x.domain(zoom.altDomain); + zoom.scale(x).updateScaleExtent(); + return; + } + if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { + x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]); + } + $$.redraw({ + withTransition: false, + withY: config.zoom_rescale, + withSubchart: false, + withEventRect: false, + withDimension: false + }); + if (d3.event.sourceEvent.type === 'mousemove') { + $$.cancelClick = true; + } + config.zoom_onzoom.call($$.api, x.orgDomain()); +}; + +return c3$1; + +}))); diff --git a/debian/missing-sources/clipboard-polyfill-be05dad.ts b/debian/missing-sources/clipboard-polyfill-be05dad.ts new file mode 100644 index 000000000..602cadb18 --- /dev/null +++ b/debian/missing-sources/clipboard-polyfill-be05dad.ts @@ -0,0 +1,270 @@ +import {Promise} from "es6-promise"; +import {DT, suppressDTWarnings} from "./DT"; + +// Debug log strings should be short, since they are copmiled into the production build. +// TODO: Compile debug logging code out of production builds? +var debugLog: (s: string) => void = function(s: string) {}; +var showWarnings = true; +var warn = (console.warn || console.log).bind(console, "[clipboard-polyfill]"); + +var TEXT_PLAIN = "text/plain"; + +export default class ClipboardPolyfill { + public static readonly DT = DT; + + public static setDebugLog(f: (s: string) => void): void { + debugLog = f; + } + + public static suppressWarnings() { + showWarnings = false; + suppressDTWarnings(); + } + + public static write(data: DT): Promise { + if (showWarnings && !data.getData(TEXT_PLAIN)) { + warn("clipboard.write() was called without a "+ + "`text/plain` data type. On some platforms, this may result in an "+ + "empty clipboard. Call clipboard.suppressWarnings() "+ + "to suppress this warning."); + } + + return new Promise((resolve, reject) => { + // Internet Explorer + if (seemToBeInIE()) { + if (writeIE(data)) { + resolve() + } else { + reject(new Error("Copying failed, possibly because the user rejected it.")); + } + return; + } + + var tracker = execCopy(data); + if (tracker.success) { + debugLog("regular execCopy worked"); + resolve(); + return; + } + + // Success detection on Edge is not possible, due to bugs in all 4 + // detection mechanisms we could try to use. Assume success. + if (navigator.userAgent.indexOf("Edge") > -1) { + debugLog("UA \"Edge\" => assuming success"); + resolve(); + return; + } + + // Fallback 1 for desktop Safari. + tracker = copyUsingTempSelection(document.body, data); + if (tracker.success) { + debugLog("copyUsingTempSelection worked"); + resolve(); + return; + } + + // Fallback 2 for desktop Safari. + tracker = copyUsingTempElem(data); + if (tracker.success) { + debugLog("copyUsingTempElem worked"); + resolve(); + return; + } + + // Fallback for iOS Safari. + var text = data.getData(TEXT_PLAIN); + if (text !== undefined && copyTextUsingDOM(text)) { + debugLog("copyTextUsingDOM worked"); + resolve(); + return; + } + + reject(new Error("Copy command failed.")); + }); + } + + public static writeText(s: string): Promise { + var dt = new DT(); + dt.setData(TEXT_PLAIN, s); + return this.write(dt); + } + + public static read(): Promise
{ + return new Promise((resolve, reject) => { + if (seemToBeInIE()) { + readIE().then( + (s: string) => resolve(DTFromText(s)), + reject + ); + return; + } + // TODO: Attempt to read using async clipboard API. + reject("Read is not supported in your browser.") + }); + } + + public static readText(): Promise { + if (seemToBeInIE()) { + return readIE(); + } + return new Promise((resolve, reject) => { + // TODO: Attempt to read using async clipboard API. + reject("Read is not supported in your browser.") + }); + } +} + +/******** Implementations ********/ + +class FallbackTracker { + public success: boolean = false; +} + +function copyListener(tracker: FallbackTracker, data: DT, e: ClipboardEvent): void { + debugLog("listener called"); + tracker.success = true; + data.forEach((value: string, key: string) => { + e.clipboardData.setData(key, value); + if (key === TEXT_PLAIN && e.clipboardData.getData(key) != value) { + debugLog("setting text/plain failed"); + tracker.success = false; + } + }); + e.preventDefault(); +} + +function execCopy(data: DT): FallbackTracker { + var tracker = new FallbackTracker(); + var listener = copyListener.bind(this, tracker, data); + + document.addEventListener("copy", listener); + try { + // We ignore the return value, since FallbackTracker tells us whether the + // listener was called. It seems that checking the return value here gives + // us no extra information in any browser. + document.execCommand("copy"); + } finally { + document.removeEventListener("copy", listener); + } + return tracker; +} + +// Create a temporary DOM element to select, so that `execCommand()` is not +// rejected. +function copyUsingTempSelection(e: HTMLElement, data: DT): FallbackTracker { + selectionSet(e); + var tracker = execCopy(data); + selectionClear(); + return tracker; +} + +// Create a temporary DOM element to select, so that `execCommand()` is not +// rejected. +function copyUsingTempElem(data: DT): FallbackTracker { + var tempElem = document.createElement("div"); + // Place some text in the elem so that Safari has something to select. + tempElem.textContent = "temporary element"; + document.body.appendChild(tempElem); + + var tracker = copyUsingTempSelection(tempElem, data); + + document.body.removeChild(tempElem); + return tracker; +} + +// Uses shadow DOM. +function copyTextUsingDOM(str: string): boolean { + debugLog("copyTextUsingDOM"); + + var tempElem = document.createElement("div"); + // Use shadow DOM if available. + var spanParent: Node = tempElem; + if (tempElem.attachShadow) { + debugLog("Using shadow DOM."); + spanParent = tempElem.attachShadow({mode: "open"}); + } + + var span = document.createElement("span"); + span.innerText = str; + // span.style.whiteSpace = "pre-wrap"; // TODO: Use `innerText` above instead? + + spanParent.appendChild(span); + document.body.appendChild(tempElem); + selectionSet(span); + + var result = document.execCommand("copy"); + + selectionClear(); + document.body.removeChild(tempElem); + + return result; +} + +/******** Selection ********/ + +function selectionSet(elem: Element): void { + var sel = document.getSelection(); + var range = document.createRange(); + range.selectNodeContents(elem); + sel.removeAllRanges(); + sel.addRange(range); +} + +function selectionClear(): void { + var sel = document.getSelection(); + sel.removeAllRanges(); +} + +/******** Convenience ********/ + +function DTFromText(s: string): DT { + var dt = new DT(); + dt.setData(TEXT_PLAIN, s); + return dt; +} + +/******** Internet Explorer ********/ + +interface IEWindow extends Window { + clipboardData: { + setData: (key: string, value: string) => boolean; + // Always results in a string: https://msdn.microsoft.com/en-us/library/ms536436(v=vs.85).aspx + getData: (key: string) => string; + } +} + +function seemToBeInIE(): boolean { + return typeof ClipboardEvent === "undefined" && + typeof (window as IEWindow).clipboardData !== "undefined" && + typeof (window as IEWindow).clipboardData.setData !== "undefined"; +} + +function writeIE(data: DT): boolean { + // IE supports text or URL, but not HTML: https://msdn.microsoft.com/en-us/library/ms536744(v=vs.85).aspx + // TODO: Write URLs to `text/uri-list`? https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types + var text = data.getData(TEXT_PLAIN); + if (text !== undefined) { + return (window as IEWindow).clipboardData.setData("Text", text); + } + + throw ("No `text/plain` value was specified."); +} + +// Returns "" if the read failed, e.g. because the user rejected the permission. +function readIE(): Promise { + return new Promise((resolve, reject) => { + var text = (window as IEWindow).clipboardData.getData("Text"); + if (text === "") { + reject(new Error("Empty clipboard or could not read plain text from clipboard")); + } else { + resolve(text); + } + }) +} + +/******** Expose `clipboard` on the global object in browser. ********/ + +// TODO: Figure out how to expose ClipboardPolyfill as self.clipboard using +// WebPack? +declare var module: any; +module.exports = ClipboardPolyfill; diff --git a/debian/missing-sources/d3-3.5.17.js b/debian/missing-sources/d3-3.5.17.js deleted file mode 100644 index aded45c44..000000000 --- a/debian/missing-sources/d3-3.5.17.js +++ /dev/null @@ -1,9554 +0,0 @@ -!function() { - var d3 = { - version: "3.5.17" - }; - var d3_arraySlice = [].slice, d3_array = function(list) { - return d3_arraySlice.call(list); - }; - var d3_document = this.document; - function d3_documentElement(node) { - return node && (node.ownerDocument || node.document || node).documentElement; - } - function d3_window(node) { - return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); - } - if (d3_document) { - try { - d3_array(d3_document.documentElement.childNodes)[0].nodeType; - } catch (e) { - d3_array = function(list) { - var i = list.length, array = new Array(i); - while (i--) array[i] = list[i]; - return array; - }; - } - } - if (!Date.now) Date.now = function() { - return +new Date(); - }; - if (d3_document) { - try { - d3_document.createElement("DIV").style.setProperty("opacity", 0, ""); - } catch (error) { - var d3_element_prototype = this.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = this.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; - d3_element_prototype.setAttribute = function(name, value) { - d3_element_setAttribute.call(this, name, value + ""); - }; - d3_element_prototype.setAttributeNS = function(space, local, value) { - d3_element_setAttributeNS.call(this, space, local, value + ""); - }; - d3_style_prototype.setProperty = function(name, value, priority) { - d3_style_setProperty.call(this, name, value + "", priority); - }; - } - } - d3.ascending = d3_ascending; - function d3_ascending(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; - } - d3.descending = function(a, b) { - return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; - }; - d3.min = function(array, f) { - var i = -1, n = array.length, a, b; - if (arguments.length === 1) { - while (++i < n) if ((b = array[i]) != null && b >= b) { - a = b; - break; - } - while (++i < n) if ((b = array[i]) != null && a > b) a = b; - } else { - while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { - a = b; - break; - } - while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; - } - return a; - }; - d3.max = function(array, f) { - var i = -1, n = array.length, a, b; - if (arguments.length === 1) { - while (++i < n) if ((b = array[i]) != null && b >= b) { - a = b; - break; - } - while (++i < n) if ((b = array[i]) != null && b > a) a = b; - } else { - while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { - a = b; - break; - } - while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; - } - return a; - }; - d3.extent = function(array, f) { - var i = -1, n = array.length, a, b, c; - if (arguments.length === 1) { - while (++i < n) if ((b = array[i]) != null && b >= b) { - a = c = b; - break; - } - while (++i < n) if ((b = array[i]) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } else { - while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { - a = c = b; - break; - } - while (++i < n) if ((b = f.call(array, array[i], i)) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } - return [ a, c ]; - }; - function d3_number(x) { - return x === null ? NaN : +x; - } - function d3_numeric(x) { - return !isNaN(x); - } - d3.sum = function(array, f) { - var s = 0, n = array.length, a, i = -1; - if (arguments.length === 1) { - while (++i < n) if (d3_numeric(a = +array[i])) s += a; - } else { - while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a; - } - return s; - }; - d3.mean = function(array, f) { - var s = 0, n = array.length, a, i = -1, j = n; - if (arguments.length === 1) { - while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j; - } else { - while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j; - } - if (j) return s / j; - }; - d3.quantile = function(values, p) { - var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; - return e ? v + e * (values[h] - v) : v; - }; - d3.median = function(array, f) { - var numbers = [], n = array.length, a, i = -1; - if (arguments.length === 1) { - while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a); - } else { - while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a); - } - if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5); - }; - d3.variance = function(array, f) { - var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0; - if (arguments.length === 1) { - while (++i < n) { - if (d3_numeric(a = d3_number(array[i]))) { - d = a - m; - m += d / ++j; - s += d * (a - m); - } - } - } else { - while (++i < n) { - if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) { - d = a - m; - m += d / ++j; - s += d * (a - m); - } - } - } - if (j > 1) return s / (j - 1); - }; - d3.deviation = function() { - var v = d3.variance.apply(this, arguments); - return v ? Math.sqrt(v) : v; - }; - function d3_bisector(compare) { - return { - left: function(a, x, lo, hi) { - if (arguments.length < 3) lo = 0; - if (arguments.length < 4) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; - } - return lo; - }, - right: function(a, x, lo, hi) { - if (arguments.length < 3) lo = 0; - if (arguments.length < 4) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; - } - return lo; - } - }; - } - var d3_bisect = d3_bisector(d3_ascending); - d3.bisectLeft = d3_bisect.left; - d3.bisect = d3.bisectRight = d3_bisect.right; - d3.bisector = function(f) { - return d3_bisector(f.length === 1 ? function(d, x) { - return d3_ascending(f(d), x); - } : f); - }; - d3.shuffle = function(array, i0, i1) { - if ((m = arguments.length) < 3) { - i1 = array.length; - if (m < 2) i0 = 0; - } - var m = i1 - i0, t, i; - while (m) { - i = Math.random() * m-- | 0; - t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t; - } - return array; - }; - d3.permute = function(array, indexes) { - var i = indexes.length, permutes = new Array(i); - while (i--) permutes[i] = array[indexes[i]]; - return permutes; - }; - d3.pairs = function(array) { - var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); - while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; - return pairs; - }; - d3.transpose = function(matrix) { - if (!(n = matrix.length)) return []; - for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) { - for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) { - row[j] = matrix[j][i]; - } - } - return transpose; - }; - function d3_transposeLength(d) { - return d.length; - } - d3.zip = function() { - return d3.transpose(arguments); - }; - d3.keys = function(map) { - var keys = []; - for (var key in map) keys.push(key); - return keys; - }; - d3.values = function(map) { - var values = []; - for (var key in map) values.push(map[key]); - return values; - }; - d3.entries = function(map) { - var entries = []; - for (var key in map) entries.push({ - key: key, - value: map[key] - }); - return entries; - }; - d3.merge = function(arrays) { - var n = arrays.length, m, i = -1, j = 0, merged, array; - while (++i < n) j += arrays[i].length; - merged = new Array(j); - while (--n >= 0) { - array = arrays[n]; - m = array.length; - while (--m >= 0) { - merged[--j] = array[m]; - } - } - return merged; - }; - var abs = Math.abs; - d3.range = function(start, stop, step) { - if (arguments.length < 3) { - step = 1; - if (arguments.length < 2) { - stop = start; - start = 0; - } - } - if ((stop - start) / step === Infinity) throw new Error("infinite range"); - var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; - start *= k, stop *= k, step *= k; - if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); - return range; - }; - function d3_range_integerScale(x) { - var k = 1; - while (x * k % 1) k *= 10; - return k; - } - function d3_class(ctor, properties) { - for (var key in properties) { - Object.defineProperty(ctor.prototype, key, { - value: properties[key], - enumerable: false - }); - } - } - d3.map = function(object, f) { - var map = new d3_Map(); - if (object instanceof d3_Map) { - object.forEach(function(key, value) { - map.set(key, value); - }); - } else if (Array.isArray(object)) { - var i = -1, n = object.length, o; - if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o); - } else { - for (var key in object) map.set(key, object[key]); - } - return map; - }; - function d3_Map() { - this._ = Object.create(null); - } - var d3_map_proto = "__proto__", d3_map_zero = "\x00"; - d3_class(d3_Map, { - has: d3_map_has, - get: function(key) { - return this._[d3_map_escape(key)]; - }, - set: function(key, value) { - return this._[d3_map_escape(key)] = value; - }, - remove: d3_map_remove, - keys: d3_map_keys, - values: function() { - var values = []; - for (var key in this._) values.push(this._[key]); - return values; - }, - entries: function() { - var entries = []; - for (var key in this._) entries.push({ - key: d3_map_unescape(key), - value: this._[key] - }); - return entries; - }, - size: d3_map_size, - empty: d3_map_empty, - forEach: function(f) { - for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]); - } - }); - function d3_map_escape(key) { - return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key; - } - function d3_map_unescape(key) { - return (key += "")[0] === d3_map_zero ? key.slice(1) : key; - } - function d3_map_has(key) { - return d3_map_escape(key) in this._; - } - function d3_map_remove(key) { - return (key = d3_map_escape(key)) in this._ && delete this._[key]; - } - function d3_map_keys() { - var keys = []; - for (var key in this._) keys.push(d3_map_unescape(key)); - return keys; - } - function d3_map_size() { - var size = 0; - for (var key in this._) ++size; - return size; - } - function d3_map_empty() { - for (var key in this._) return false; - return true; - } - d3.nest = function() { - var nest = {}, keys = [], sortKeys = [], sortValues, rollup; - function map(mapType, array, depth) { - if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; - var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; - while (++i < n) { - if (values = valuesByKey.get(keyValue = key(object = array[i]))) { - values.push(object); - } else { - valuesByKey.set(keyValue, [ object ]); - } - } - if (mapType) { - object = mapType(); - setter = function(keyValue, values) { - object.set(keyValue, map(mapType, values, depth)); - }; - } else { - object = {}; - setter = function(keyValue, values) { - object[keyValue] = map(mapType, values, depth); - }; - } - valuesByKey.forEach(setter); - return object; - } - function entries(map, depth) { - if (depth >= keys.length) return map; - var array = [], sortKey = sortKeys[depth++]; - map.forEach(function(key, keyMap) { - array.push({ - key: key, - values: entries(keyMap, depth) - }); - }); - return sortKey ? array.sort(function(a, b) { - return sortKey(a.key, b.key); - }) : array; - } - nest.map = function(array, mapType) { - return map(mapType, array, 0); - }; - nest.entries = function(array) { - return entries(map(d3.map, array, 0), 0); - }; - nest.key = function(d) { - keys.push(d); - return nest; - }; - nest.sortKeys = function(order) { - sortKeys[keys.length - 1] = order; - return nest; - }; - nest.sortValues = function(order) { - sortValues = order; - return nest; - }; - nest.rollup = function(f) { - rollup = f; - return nest; - }; - return nest; - }; - d3.set = function(array) { - var set = new d3_Set(); - if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); - return set; - }; - function d3_Set() { - this._ = Object.create(null); - } - d3_class(d3_Set, { - has: d3_map_has, - add: function(key) { - this._[d3_map_escape(key += "")] = true; - return key; - }, - remove: d3_map_remove, - values: d3_map_keys, - size: d3_map_size, - empty: d3_map_empty, - forEach: function(f) { - for (var key in this._) f.call(this, d3_map_unescape(key)); - } - }); - d3.behavior = {}; - function d3_identity(d) { - return d; - } - d3.rebind = function(target, source) { - var i = 1, n = arguments.length, method; - while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); - return target; - }; - function d3_rebind(target, source, method) { - return function() { - var value = method.apply(source, arguments); - return value === source ? target : value; - }; - } - function d3_vendorSymbol(object, name) { - if (name in object) return name; - name = name.charAt(0).toUpperCase() + name.slice(1); - for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { - var prefixName = d3_vendorPrefixes[i] + name; - if (prefixName in object) return prefixName; - } - } - var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; - function d3_noop() {} - d3.dispatch = function() { - var dispatch = new d3_dispatch(), i = -1, n = arguments.length; - while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); - return dispatch; - }; - function d3_dispatch() {} - d3_dispatch.prototype.on = function(type, listener) { - var i = type.indexOf("."), name = ""; - if (i >= 0) { - name = type.slice(i + 1); - type = type.slice(0, i); - } - if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); - if (arguments.length === 2) { - if (listener == null) for (type in this) { - if (this.hasOwnProperty(type)) this[type].on(name, null); - } - return this; - } - }; - function d3_dispatch_event(dispatch) { - var listeners = [], listenerByName = new d3_Map(); - function event() { - var z = listeners, i = -1, n = z.length, l; - while (++i < n) if (l = z[i].on) l.apply(this, arguments); - return dispatch; - } - event.on = function(name, listener) { - var l = listenerByName.get(name), i; - if (arguments.length < 2) return l && l.on; - if (l) { - l.on = null; - listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); - listenerByName.remove(name); - } - if (listener) listeners.push(listenerByName.set(name, { - on: listener - })); - return dispatch; - }; - return event; - } - d3.event = null; - function d3_eventPreventDefault() { - d3.event.preventDefault(); - } - function d3_eventSource() { - var e = d3.event, s; - while (s = e.sourceEvent) e = s; - return e; - } - function d3_eventDispatch(target) { - var dispatch = new d3_dispatch(), i = 0, n = arguments.length; - while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); - dispatch.of = function(thiz, argumentz) { - return function(e1) { - try { - var e0 = e1.sourceEvent = d3.event; - e1.target = target; - d3.event = e1; - dispatch[e1.type].apply(thiz, argumentz); - } finally { - d3.event = e0; - } - }; - }; - return dispatch; - } - d3.requote = function(s) { - return s.replace(d3_requote_re, "\\$&"); - }; - var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; - var d3_subclass = {}.__proto__ ? function(object, prototype) { - object.__proto__ = prototype; - } : function(object, prototype) { - for (var property in prototype) object[property] = prototype[property]; - }; - function d3_selection(groups) { - d3_subclass(groups, d3_selectionPrototype); - return groups; - } - var d3_select = function(s, n) { - return n.querySelector(s); - }, d3_selectAll = function(s, n) { - return n.querySelectorAll(s); - }, d3_selectMatches = function(n, s) { - var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")]; - d3_selectMatches = function(n, s) { - return d3_selectMatcher.call(n, s); - }; - return d3_selectMatches(n, s); - }; - if (typeof Sizzle === "function") { - d3_select = function(s, n) { - return Sizzle(s, n)[0] || null; - }; - d3_selectAll = Sizzle; - d3_selectMatches = Sizzle.matchesSelector; - } - d3.selection = function() { - return d3.select(d3_document.documentElement); - }; - var d3_selectionPrototype = d3.selection.prototype = []; - d3_selectionPrototype.select = function(selector) { - var subgroups = [], subgroup, subnode, group, node; - selector = d3_selection_selector(selector); - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - subgroup.parentNode = (group = this[j]).parentNode; - for (var i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroup.push(subnode = selector.call(node, node.__data__, i, j)); - if (subnode && "__data__" in node) subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_selector(selector) { - return typeof selector === "function" ? selector : function() { - return d3_select(selector, this); - }; - } - d3_selectionPrototype.selectAll = function(selector) { - var subgroups = [], subgroup, node; - selector = d3_selection_selectorAll(selector); - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); - subgroup.parentNode = node; - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_selectorAll(selector) { - return typeof selector === "function" ? selector : function() { - return d3_selectAll(selector, this); - }; - } - var d3_nsXhtml = "http://www.w3.org/1999/xhtml"; - var d3_nsPrefix = { - svg: "http://www.w3.org/2000/svg", - xhtml: d3_nsXhtml, - xlink: "http://www.w3.org/1999/xlink", - xml: "http://www.w3.org/XML/1998/namespace", - xmlns: "http://www.w3.org/2000/xmlns/" - }; - d3.ns = { - prefix: d3_nsPrefix, - qualify: function(name) { - var i = name.indexOf(":"), prefix = name; - if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); - return d3_nsPrefix.hasOwnProperty(prefix) ? { - space: d3_nsPrefix[prefix], - local: name - } : name; - } - }; - d3_selectionPrototype.attr = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") { - var node = this.node(); - name = d3.ns.qualify(name); - return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); - } - for (value in name) this.each(d3_selection_attr(value, name[value])); - return this; - } - return this.each(d3_selection_attr(name, value)); - }; - function d3_selection_attr(name, value) { - name = d3.ns.qualify(name); - function attrNull() { - this.removeAttribute(name); - } - function attrNullNS() { - this.removeAttributeNS(name.space, name.local); - } - function attrConstant() { - this.setAttribute(name, value); - } - function attrConstantNS() { - this.setAttributeNS(name.space, name.local, value); - } - function attrFunction() { - var x = value.apply(this, arguments); - if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); - } - function attrFunctionNS() { - var x = value.apply(this, arguments); - if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); - } - return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; - } - function d3_collapse(s) { - return s.trim().replace(/\s+/g, " "); - } - d3_selectionPrototype.classed = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") { - var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; - if (value = node.classList) { - while (++i < n) if (!value.contains(name[i])) return false; - } else { - value = node.getAttribute("class"); - while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; - } - return true; - } - for (value in name) this.each(d3_selection_classed(value, name[value])); - return this; - } - return this.each(d3_selection_classed(name, value)); - }; - function d3_selection_classedRe(name) { - return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); - } - function d3_selection_classes(name) { - return (name + "").trim().split(/^|\s+/); - } - function d3_selection_classed(name, value) { - name = d3_selection_classes(name).map(d3_selection_classedName); - var n = name.length; - function classedConstant() { - var i = -1; - while (++i < n) name[i](this, value); - } - function classedFunction() { - var i = -1, x = value.apply(this, arguments); - while (++i < n) name[i](this, x); - } - return typeof value === "function" ? classedFunction : classedConstant; - } - function d3_selection_classedName(name) { - var re = d3_selection_classedRe(name); - return function(node, value) { - if (c = node.classList) return value ? c.add(name) : c.remove(name); - var c = node.getAttribute("class") || ""; - if (value) { - re.lastIndex = 0; - if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); - } else { - node.setAttribute("class", d3_collapse(c.replace(re, " "))); - } - }; - } - d3_selectionPrototype.style = function(name, value, priority) { - var n = arguments.length; - if (n < 3) { - if (typeof name !== "string") { - if (n < 2) value = ""; - for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); - return this; - } - if (n < 2) { - var node = this.node(); - return d3_window(node).getComputedStyle(node, null).getPropertyValue(name); - } - priority = ""; - } - return this.each(d3_selection_style(name, value, priority)); - }; - function d3_selection_style(name, value, priority) { - function styleNull() { - this.style.removeProperty(name); - } - function styleConstant() { - this.style.setProperty(name, value, priority); - } - function styleFunction() { - var x = value.apply(this, arguments); - if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); - } - return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; - } - d3_selectionPrototype.property = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") return this.node()[name]; - for (value in name) this.each(d3_selection_property(value, name[value])); - return this; - } - return this.each(d3_selection_property(name, value)); - }; - function d3_selection_property(name, value) { - function propertyNull() { - delete this[name]; - } - function propertyConstant() { - this[name] = value; - } - function propertyFunction() { - var x = value.apply(this, arguments); - if (x == null) delete this[name]; else this[name] = x; - } - return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; - } - d3_selectionPrototype.text = function(value) { - return arguments.length ? this.each(typeof value === "function" ? function() { - var v = value.apply(this, arguments); - this.textContent = v == null ? "" : v; - } : value == null ? function() { - this.textContent = ""; - } : function() { - this.textContent = value; - }) : this.node().textContent; - }; - d3_selectionPrototype.html = function(value) { - return arguments.length ? this.each(typeof value === "function" ? function() { - var v = value.apply(this, arguments); - this.innerHTML = v == null ? "" : v; - } : value == null ? function() { - this.innerHTML = ""; - } : function() { - this.innerHTML = value; - }) : this.node().innerHTML; - }; - d3_selectionPrototype.append = function(name) { - name = d3_selection_creator(name); - return this.select(function() { - return this.appendChild(name.apply(this, arguments)); - }); - }; - function d3_selection_creator(name) { - function create() { - var document = this.ownerDocument, namespace = this.namespaceURI; - return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name); - } - function createNS() { - return this.ownerDocument.createElementNS(name.space, name.local); - } - return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create; - } - d3_selectionPrototype.insert = function(name, before) { - name = d3_selection_creator(name); - before = d3_selection_selector(before); - return this.select(function() { - return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); - }); - }; - d3_selectionPrototype.remove = function() { - return this.each(d3_selectionRemove); - }; - function d3_selectionRemove() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); - } - d3_selectionPrototype.data = function(value, key) { - var i = -1, n = this.length, group, node; - if (!arguments.length) { - value = new Array(n = (group = this[0]).length); - while (++i < n) { - if (node = group[i]) { - value[i] = node.__data__; - } - } - return value; - } - function bind(group, groupData) { - var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; - if (key) { - var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue; - for (i = -1; ++i < n; ) { - if (node = group[i]) { - if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) { - exitNodes[i] = node; - } else { - nodeByKeyValue.set(keyValue, node); - } - keyValues[i] = keyValue; - } - } - for (i = -1; ++i < m; ) { - if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { - enterNodes[i] = d3_selection_dataNode(nodeData); - } else if (node !== true) { - updateNodes[i] = node; - node.__data__ = nodeData; - } - nodeByKeyValue.set(keyValue, true); - } - for (i = -1; ++i < n; ) { - if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) { - exitNodes[i] = group[i]; - } - } - } else { - for (i = -1; ++i < n0; ) { - node = group[i]; - nodeData = groupData[i]; - if (node) { - node.__data__ = nodeData; - updateNodes[i] = node; - } else { - enterNodes[i] = d3_selection_dataNode(nodeData); - } - } - for (;i < m; ++i) { - enterNodes[i] = d3_selection_dataNode(groupData[i]); - } - for (;i < n; ++i) { - exitNodes[i] = group[i]; - } - } - enterNodes.update = updateNodes; - enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; - enter.push(enterNodes); - update.push(updateNodes); - exit.push(exitNodes); - } - var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); - if (typeof value === "function") { - while (++i < n) { - bind(group = this[i], value.call(group, group.parentNode.__data__, i)); - } - } else { - while (++i < n) { - bind(group = this[i], value); - } - } - update.enter = function() { - return enter; - }; - update.exit = function() { - return exit; - }; - return update; - }; - function d3_selection_dataNode(data) { - return { - __data__: data - }; - } - d3_selectionPrototype.datum = function(value) { - return arguments.length ? this.property("__data__", value) : this.property("__data__"); - }; - d3_selectionPrototype.filter = function(filter) { - var subgroups = [], subgroup, group, node; - if (typeof filter !== "function") filter = d3_selection_filter(filter); - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - subgroup.parentNode = (group = this[j]).parentNode; - for (var i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { - subgroup.push(node); - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_filter(selector) { - return function() { - return d3_selectMatches(this, selector); - }; - } - d3_selectionPrototype.order = function() { - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { - if (node = group[i]) { - if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); - next = node; - } - } - } - return this; - }; - d3_selectionPrototype.sort = function(comparator) { - comparator = d3_selection_sortComparator.apply(this, arguments); - for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); - return this.order(); - }; - function d3_selection_sortComparator(comparator) { - if (!arguments.length) comparator = d3_ascending; - return function(a, b) { - return a && b ? comparator(a.__data__, b.__data__) : !a - !b; - }; - } - d3_selectionPrototype.each = function(callback) { - return d3_selection_each(this, function(node, i, j) { - callback.call(node, node.__data__, i, j); - }); - }; - function d3_selection_each(groups, callback) { - for (var j = 0, m = groups.length; j < m; j++) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { - if (node = group[i]) callback(node, i, j); - } - } - return groups; - } - d3_selectionPrototype.call = function(callback) { - var args = d3_array(arguments); - callback.apply(args[0] = this, args); - return this; - }; - d3_selectionPrototype.empty = function() { - return !this.node(); - }; - d3_selectionPrototype.node = function() { - for (var j = 0, m = this.length; j < m; j++) { - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - var node = group[i]; - if (node) return node; - } - } - return null; - }; - d3_selectionPrototype.size = function() { - var n = 0; - d3_selection_each(this, function() { - ++n; - }); - return n; - }; - function d3_selection_enter(selection) { - d3_subclass(selection, d3_selection_enterPrototype); - return selection; - } - var d3_selection_enterPrototype = []; - d3.selection.enter = d3_selection_enter; - d3.selection.enter.prototype = d3_selection_enterPrototype; - d3_selection_enterPrototype.append = d3_selectionPrototype.append; - d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; - d3_selection_enterPrototype.node = d3_selectionPrototype.node; - d3_selection_enterPrototype.call = d3_selectionPrototype.call; - d3_selection_enterPrototype.size = d3_selectionPrototype.size; - d3_selection_enterPrototype.select = function(selector) { - var subgroups = [], subgroup, subnode, upgroup, group, node; - for (var j = -1, m = this.length; ++j < m; ) { - upgroup = (group = this[j]).update; - subgroups.push(subgroup = []); - subgroup.parentNode = group.parentNode; - for (var i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); - subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } - return d3_selection(subgroups); - }; - d3_selection_enterPrototype.insert = function(name, before) { - if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); - return d3_selectionPrototype.insert.call(this, name, before); - }; - function d3_selection_enterInsertBefore(enter) { - var i0, j0; - return function(d, i, j) { - var group = enter[j].update, n = group.length, node; - if (j != j0) j0 = j, i0 = 0; - if (i >= i0) i0 = i + 1; - while (!(node = group[i0]) && ++i0 < n) ; - return node; - }; - } - d3.select = function(node) { - var group; - if (typeof node === "string") { - group = [ d3_select(node, d3_document) ]; - group.parentNode = d3_document.documentElement; - } else { - group = [ node ]; - group.parentNode = d3_documentElement(node); - } - return d3_selection([ group ]); - }; - d3.selectAll = function(nodes) { - var group; - if (typeof nodes === "string") { - group = d3_array(d3_selectAll(nodes, d3_document)); - group.parentNode = d3_document.documentElement; - } else { - group = d3_array(nodes); - group.parentNode = null; - } - return d3_selection([ group ]); - }; - d3_selectionPrototype.on = function(type, listener, capture) { - var n = arguments.length; - if (n < 3) { - if (typeof type !== "string") { - if (n < 2) listener = false; - for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); - return this; - } - if (n < 2) return (n = this.node()["__on" + type]) && n._; - capture = false; - } - return this.each(d3_selection_on(type, listener, capture)); - }; - function d3_selection_on(type, listener, capture) { - var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; - if (i > 0) type = type.slice(0, i); - var filter = d3_selection_onFilters.get(type); - if (filter) type = filter, wrap = d3_selection_onFilter; - function onRemove() { - var l = this[name]; - if (l) { - this.removeEventListener(type, l, l.$); - delete this[name]; - } - } - function onAdd() { - var l = wrap(listener, d3_array(arguments)); - onRemove.call(this); - this.addEventListener(type, this[name] = l, l.$ = capture); - l._ = listener; - } - function removeAll() { - var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; - for (var name in this) { - if (match = name.match(re)) { - var l = this[name]; - this.removeEventListener(match[1], l, l.$); - delete this[name]; - } - } - } - return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; - } - var d3_selection_onFilters = d3.map({ - mouseenter: "mouseover", - mouseleave: "mouseout" - }); - if (d3_document) { - d3_selection_onFilters.forEach(function(k) { - if ("on" + k in d3_document) d3_selection_onFilters.remove(k); - }); - } - function d3_selection_onListener(listener, argumentz) { - return function(e) { - var o = d3.event; - d3.event = e; - argumentz[0] = this.__data__; - try { - listener.apply(this, argumentz); - } finally { - d3.event = o; - } - }; - } - function d3_selection_onFilter(listener, argumentz) { - var l = d3_selection_onListener(listener, argumentz); - return function(e) { - var target = this, related = e.relatedTarget; - if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { - l.call(target, e); - } - }; - } - var d3_event_dragSelect, d3_event_dragId = 0; - function d3_event_dragSuppress(node) { - var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window(node)).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); - if (d3_event_dragSelect == null) { - d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect"); - } - if (d3_event_dragSelect) { - var style = d3_documentElement(node).style, select = style[d3_event_dragSelect]; - style[d3_event_dragSelect] = "none"; - } - return function(suppressClick) { - w.on(name, null); - if (d3_event_dragSelect) style[d3_event_dragSelect] = select; - if (suppressClick) { - var off = function() { - w.on(click, null); - }; - w.on(click, function() { - d3_eventPreventDefault(); - off(); - }, true); - setTimeout(off, 0); - } - }; - } - d3.mouse = function(container) { - return d3_mousePoint(container, d3_eventSource()); - }; - var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; - function d3_mousePoint(container, e) { - if (e.changedTouches) e = e.changedTouches[0]; - var svg = container.ownerSVGElement || container; - if (svg.createSVGPoint) { - var point = svg.createSVGPoint(); - if (d3_mouse_bug44083 < 0) { - var window = d3_window(container); - if (window.scrollX || window.scrollY) { - svg = d3.select("body").append("svg").style({ - position: "absolute", - top: 0, - left: 0, - margin: 0, - padding: 0, - border: "none" - }, "important"); - var ctm = svg[0][0].getScreenCTM(); - d3_mouse_bug44083 = !(ctm.f || ctm.e); - svg.remove(); - } - } - if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, - point.y = e.clientY; - point = point.matrixTransform(container.getScreenCTM().inverse()); - return [ point.x, point.y ]; - } - var rect = container.getBoundingClientRect(); - return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; - } - d3.touch = function(container, touches, identifier) { - if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; - if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { - if ((touch = touches[i]).identifier === identifier) { - return d3_mousePoint(container, touch); - } - } - }; - d3.behavior.drag = function() { - var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend"); - function drag() { - this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); - } - function dragstart(id, position, subject, move, end) { - return function() { - var that = this, target = d3.event.target.correspondingElement || d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId); - if (origin) { - dragOffset = origin.apply(that, arguments); - dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; - } else { - dragOffset = [ 0, 0 ]; - } - dispatch({ - type: "dragstart" - }); - function moved() { - var position1 = position(parent, dragId), dx, dy; - if (!position1) return; - dx = position1[0] - position0[0]; - dy = position1[1] - position0[1]; - dragged |= dx | dy; - position0 = position1; - dispatch({ - type: "drag", - x: position1[0] + dragOffset[0], - y: position1[1] + dragOffset[1], - dx: dx, - dy: dy - }); - } - function ended() { - if (!position(parent, dragId)) return; - dragSubject.on(move + dragName, null).on(end + dragName, null); - dragRestore(dragged); - dispatch({ - type: "dragend" - }); - } - }; - } - drag.origin = function(x) { - if (!arguments.length) return origin; - origin = x; - return drag; - }; - return d3.rebind(drag, event, "on"); - }; - function d3_behavior_dragTouchId() { - return d3.event.changedTouches[0].identifier; - } - d3.touches = function(container, touches) { - if (arguments.length < 2) touches = d3_eventSource().touches; - return touches ? d3_array(touches).map(function(touch) { - var point = d3_mousePoint(container, touch); - point.identifier = touch.identifier; - return point; - }) : []; - }; - var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π; - function d3_sgn(x) { - return x > 0 ? 1 : x < 0 ? -1 : 0; - } - function d3_cross2d(a, b, c) { - return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); - } - function d3_acos(x) { - return x > 1 ? 0 : x < -1 ? π : Math.acos(x); - } - function d3_asin(x) { - return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); - } - function d3_sinh(x) { - return ((x = Math.exp(x)) - 1 / x) / 2; - } - function d3_cosh(x) { - return ((x = Math.exp(x)) + 1 / x) / 2; - } - function d3_tanh(x) { - return ((x = Math.exp(2 * x)) - 1) / (x + 1); - } - function d3_haversin(x) { - return (x = Math.sin(x / 2)) * x; - } - var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; - d3.interpolateZoom = function(p0, p1) { - var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2], dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, i, S; - if (d2 < ε2) { - S = Math.log(w1 / w0) / ρ; - i = function(t) { - return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ]; - }; - } else { - var d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); - S = (r1 - r0) / ρ; - i = function(t) { - var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); - return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; - }; - } - i.duration = S * 1e3; - return i; - }; - d3.behavior.zoom = function() { - var view = { - x: 0, - y: 0, - k: 1 - }, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; - if (!d3_behavior_zoomWheel) { - d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { - return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); - }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { - return d3.event.wheelDelta; - }, "mousewheel") : (d3_behavior_zoomDelta = function() { - return -d3.event.detail; - }, "MozMousePixelScroll"); - } - function zoom(g) { - g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); - } - zoom.event = function(g) { - g.each(function() { - var dispatch = event.of(this, arguments), view1 = view; - if (d3_transitionInheritId) { - d3.select(this).transition().each("start.zoom", function() { - view = this.__chart__ || { - x: 0, - y: 0, - k: 1 - }; - zoomstarted(dispatch); - }).tween("zoom:zoom", function() { - var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); - return function(t) { - var l = i(t), k = dx / l[2]; - this.__chart__ = view = { - x: cx - l[0] * k, - y: cy - l[1] * k, - k: k - }; - zoomed(dispatch); - }; - }).each("interrupt.zoom", function() { - zoomended(dispatch); - }).each("end.zoom", function() { - zoomended(dispatch); - }); - } else { - this.__chart__ = view; - zoomstarted(dispatch); - zoomed(dispatch); - zoomended(dispatch); - } - }); - }; - zoom.translate = function(_) { - if (!arguments.length) return [ view.x, view.y ]; - view = { - x: +_[0], - y: +_[1], - k: view.k - }; - rescale(); - return zoom; - }; - zoom.scale = function(_) { - if (!arguments.length) return view.k; - view = { - x: view.x, - y: view.y, - k: null - }; - scaleTo(+_); - rescale(); - return zoom; - }; - zoom.scaleExtent = function(_) { - if (!arguments.length) return scaleExtent; - scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; - return zoom; - }; - zoom.center = function(_) { - if (!arguments.length) return center; - center = _ && [ +_[0], +_[1] ]; - return zoom; - }; - zoom.size = function(_) { - if (!arguments.length) return size; - size = _ && [ +_[0], +_[1] ]; - return zoom; - }; - zoom.duration = function(_) { - if (!arguments.length) return duration; - duration = +_; - return zoom; - }; - zoom.x = function(z) { - if (!arguments.length) return x1; - x1 = z; - x0 = z.copy(); - view = { - x: 0, - y: 0, - k: 1 - }; - return zoom; - }; - zoom.y = function(z) { - if (!arguments.length) return y1; - y1 = z; - y0 = z.copy(); - view = { - x: 0, - y: 0, - k: 1 - }; - return zoom; - }; - function location(p) { - return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; - } - function point(l) { - return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; - } - function scaleTo(s) { - view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); - } - function translateTo(p, l) { - l = point(l); - view.x += p[0] - l[0]; - view.y += p[1] - l[1]; - } - function zoomTo(that, p, l, k) { - that.__chart__ = { - x: view.x, - y: view.y, - k: view.k - }; - scaleTo(Math.pow(2, k)); - translateTo(center0 = p, l); - that = d3.select(that); - if (duration > 0) that = that.transition().duration(duration); - that.call(zoom.event); - } - function rescale() { - if (x1) x1.domain(x0.range().map(function(x) { - return (x - view.x) / view.k; - }).map(x0.invert)); - if (y1) y1.domain(y0.range().map(function(y) { - return (y - view.y) / view.k; - }).map(y0.invert)); - } - function zoomstarted(dispatch) { - if (!zooming++) dispatch({ - type: "zoomstart" - }); - } - function zoomed(dispatch) { - rescale(); - dispatch({ - type: "zoom", - scale: view.k, - translate: [ view.x, view.y ] - }); - } - function zoomended(dispatch) { - if (!--zooming) dispatch({ - type: "zoomend" - }), center0 = null; - } - function mousedowned() { - var that = this, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that); - d3_selection_interrupt.call(that); - zoomstarted(dispatch); - function moved() { - dragged = 1; - translateTo(d3.mouse(that), location0); - zoomed(dispatch); - } - function ended() { - subject.on(mousemove, null).on(mouseup, null); - dragRestore(dragged); - zoomended(dispatch); - } - } - function touchstarted() { - var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress(that); - started(); - zoomstarted(dispatch); - subject.on(mousedown, null).on(touchstart, started); - function relocate() { - var touches = d3.touches(that); - scale0 = view.k; - touches.forEach(function(t) { - if (t.identifier in locations0) locations0[t.identifier] = location(t); - }); - return touches; - } - function started() { - var target = d3.event.target; - d3.select(target).on(touchmove, moved).on(touchend, ended); - targets.push(target); - var changed = d3.event.changedTouches; - for (var i = 0, n = changed.length; i < n; ++i) { - locations0[changed[i].identifier] = null; - } - var touches = relocate(), now = Date.now(); - if (touches.length === 1) { - if (now - touchtime < 500) { - var p = touches[0]; - zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1); - d3_eventPreventDefault(); - } - touchtime = now; - } else if (touches.length > 1) { - var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; - distance0 = dx * dx + dy * dy; - } - } - function moved() { - var touches = d3.touches(that), p0, l0, p1, l1; - d3_selection_interrupt.call(that); - for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { - p1 = touches[i]; - if (l1 = locations0[p1.identifier]) { - if (l0) break; - p0 = p1, l0 = l1; - } - } - if (l1) { - var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); - p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; - l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; - scaleTo(scale1 * scale0); - } - touchtime = null; - translateTo(p0, l0); - zoomed(dispatch); - } - function ended() { - if (d3.event.touches.length) { - var changed = d3.event.changedTouches; - for (var i = 0, n = changed.length; i < n; ++i) { - delete locations0[changed[i].identifier]; - } - for (var identifier in locations0) { - return void relocate(); - } - } - d3.selectAll(targets).on(zoomName, null); - subject.on(mousedown, mousedowned).on(touchstart, touchstarted); - dragRestore(); - zoomended(dispatch); - } - } - function mousewheeled() { - var dispatch = event.of(this, arguments); - if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), - translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch); - mousewheelTimer = setTimeout(function() { - mousewheelTimer = null; - zoomended(dispatch); - }, 50); - d3_eventPreventDefault(); - scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); - translateTo(center0, translate0); - zoomed(dispatch); - } - function dblclicked() { - var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2; - zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1); - } - return d3.rebind(zoom, event, "on"); - }; - var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel; - d3.color = d3_color; - function d3_color() {} - d3_color.prototype.toString = function() { - return this.rgb() + ""; - }; - d3.hsl = d3_hsl; - function d3_hsl(h, s, l) { - return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l); - } - var d3_hslPrototype = d3_hsl.prototype = new d3_color(); - d3_hslPrototype.brighter = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return new d3_hsl(this.h, this.s, this.l / k); - }; - d3_hslPrototype.darker = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return new d3_hsl(this.h, this.s, k * this.l); - }; - d3_hslPrototype.rgb = function() { - return d3_hsl_rgb(this.h, this.s, this.l); - }; - function d3_hsl_rgb(h, s, l) { - var m1, m2; - h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; - s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; - l = l < 0 ? 0 : l > 1 ? 1 : l; - m2 = l <= .5 ? l * (1 + s) : l + s - l * s; - m1 = 2 * l - m2; - function v(h) { - if (h > 360) h -= 360; else if (h < 0) h += 360; - if (h < 60) return m1 + (m2 - m1) * h / 60; - if (h < 180) return m2; - if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; - return m1; - } - function vv(h) { - return Math.round(v(h) * 255); - } - return new d3_rgb(vv(h + 120), vv(h), vv(h - 120)); - } - d3.hcl = d3_hcl; - function d3_hcl(h, c, l) { - return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l); - } - var d3_hclPrototype = d3_hcl.prototype = new d3_color(); - d3_hclPrototype.brighter = function(k) { - return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); - }; - d3_hclPrototype.darker = function(k) { - return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); - }; - d3_hclPrototype.rgb = function() { - return d3_hcl_lab(this.h, this.c, this.l).rgb(); - }; - function d3_hcl_lab(h, c, l) { - if (isNaN(h)) h = 0; - if (isNaN(c)) c = 0; - return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); - } - d3.lab = d3_lab; - function d3_lab(l, a, b) { - return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b); - } - var d3_lab_K = 18; - var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; - var d3_labPrototype = d3_lab.prototype = new d3_color(); - d3_labPrototype.brighter = function(k) { - return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); - }; - d3_labPrototype.darker = function(k) { - return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); - }; - d3_labPrototype.rgb = function() { - return d3_lab_rgb(this.l, this.a, this.b); - }; - function d3_lab_rgb(l, a, b) { - var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; - x = d3_lab_xyz(x) * d3_lab_X; - y = d3_lab_xyz(y) * d3_lab_Y; - z = d3_lab_xyz(z) * d3_lab_Z; - return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); - } - function d3_lab_hcl(l, a, b) { - return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l); - } - function d3_lab_xyz(x) { - return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; - } - function d3_xyz_lab(x) { - return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; - } - function d3_xyz_rgb(r) { - return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); - } - d3.rgb = d3_rgb; - function d3_rgb(r, g, b) { - return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b); - } - function d3_rgbNumber(value) { - return new d3_rgb(value >> 16, value >> 8 & 255, value & 255); - } - function d3_rgbString(value) { - return d3_rgbNumber(value) + ""; - } - var d3_rgbPrototype = d3_rgb.prototype = new d3_color(); - d3_rgbPrototype.brighter = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - var r = this.r, g = this.g, b = this.b, i = 30; - if (!r && !g && !b) return new d3_rgb(i, i, i); - if (r && r < i) r = i; - if (g && g < i) g = i; - if (b && b < i) b = i; - return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k)); - }; - d3_rgbPrototype.darker = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return new d3_rgb(k * this.r, k * this.g, k * this.b); - }; - d3_rgbPrototype.hsl = function() { - return d3_rgb_hsl(this.r, this.g, this.b); - }; - d3_rgbPrototype.toString = function() { - return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); - }; - function d3_rgb_hex(v) { - return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); - } - function d3_rgb_parse(format, rgb, hsl) { - var r = 0, g = 0, b = 0, m1, m2, color; - m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase()); - if (m1) { - m2 = m1[2].split(","); - switch (m1[1]) { - case "hsl": - { - return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); - } - - case "rgb": - { - return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); - } - } - } - if (color = d3_rgb_names.get(format)) { - return rgb(color.r, color.g, color.b); - } - if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) { - if (format.length === 4) { - r = (color & 3840) >> 4; - r = r >> 4 | r; - g = color & 240; - g = g >> 4 | g; - b = color & 15; - b = b << 4 | b; - } else if (format.length === 7) { - r = (color & 16711680) >> 16; - g = (color & 65280) >> 8; - b = color & 255; - } - } - return rgb(r, g, b); - } - function d3_rgb_hsl(r, g, b) { - var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; - if (d) { - s = l < .5 ? d / (max + min) : d / (2 - max - min); - if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; - h *= 60; - } else { - h = NaN; - s = l > 0 && l < 1 ? 0 : h; - } - return new d3_hsl(h, s, l); - } - function d3_rgb_lab(r, g, b) { - r = d3_rgb_xyz(r); - g = d3_rgb_xyz(g); - b = d3_rgb_xyz(b); - var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); - return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); - } - function d3_rgb_xyz(r) { - return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); - } - function d3_rgb_parseNumber(c) { - var f = parseFloat(c); - return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; - } - var d3_rgb_names = d3.map({ - aliceblue: 15792383, - antiquewhite: 16444375, - aqua: 65535, - aquamarine: 8388564, - azure: 15794175, - beige: 16119260, - bisque: 16770244, - black: 0, - blanchedalmond: 16772045, - blue: 255, - blueviolet: 9055202, - brown: 10824234, - burlywood: 14596231, - cadetblue: 6266528, - chartreuse: 8388352, - chocolate: 13789470, - coral: 16744272, - cornflowerblue: 6591981, - cornsilk: 16775388, - crimson: 14423100, - cyan: 65535, - darkblue: 139, - darkcyan: 35723, - darkgoldenrod: 12092939, - darkgray: 11119017, - darkgreen: 25600, - darkgrey: 11119017, - darkkhaki: 12433259, - darkmagenta: 9109643, - darkolivegreen: 5597999, - darkorange: 16747520, - darkorchid: 10040012, - darkred: 9109504, - darksalmon: 15308410, - darkseagreen: 9419919, - darkslateblue: 4734347, - darkslategray: 3100495, - darkslategrey: 3100495, - darkturquoise: 52945, - darkviolet: 9699539, - deeppink: 16716947, - deepskyblue: 49151, - dimgray: 6908265, - dimgrey: 6908265, - dodgerblue: 2003199, - firebrick: 11674146, - floralwhite: 16775920, - forestgreen: 2263842, - fuchsia: 16711935, - gainsboro: 14474460, - ghostwhite: 16316671, - gold: 16766720, - goldenrod: 14329120, - gray: 8421504, - green: 32768, - greenyellow: 11403055, - grey: 8421504, - honeydew: 15794160, - hotpink: 16738740, - indianred: 13458524, - indigo: 4915330, - ivory: 16777200, - khaki: 15787660, - lavender: 15132410, - lavenderblush: 16773365, - lawngreen: 8190976, - lemonchiffon: 16775885, - lightblue: 11393254, - lightcoral: 15761536, - lightcyan: 14745599, - lightgoldenrodyellow: 16448210, - lightgray: 13882323, - lightgreen: 9498256, - lightgrey: 13882323, - lightpink: 16758465, - lightsalmon: 16752762, - lightseagreen: 2142890, - lightskyblue: 8900346, - lightslategray: 7833753, - lightslategrey: 7833753, - lightsteelblue: 11584734, - lightyellow: 16777184, - lime: 65280, - limegreen: 3329330, - linen: 16445670, - magenta: 16711935, - maroon: 8388608, - mediumaquamarine: 6737322, - mediumblue: 205, - mediumorchid: 12211667, - mediumpurple: 9662683, - mediumseagreen: 3978097, - mediumslateblue: 8087790, - mediumspringgreen: 64154, - mediumturquoise: 4772300, - mediumvioletred: 13047173, - midnightblue: 1644912, - mintcream: 16121850, - mistyrose: 16770273, - moccasin: 16770229, - navajowhite: 16768685, - navy: 128, - oldlace: 16643558, - olive: 8421376, - olivedrab: 7048739, - orange: 16753920, - orangered: 16729344, - orchid: 14315734, - palegoldenrod: 15657130, - palegreen: 10025880, - paleturquoise: 11529966, - palevioletred: 14381203, - papayawhip: 16773077, - peachpuff: 16767673, - peru: 13468991, - pink: 16761035, - plum: 14524637, - powderblue: 11591910, - purple: 8388736, - rebeccapurple: 6697881, - red: 16711680, - rosybrown: 12357519, - royalblue: 4286945, - saddlebrown: 9127187, - salmon: 16416882, - sandybrown: 16032864, - seagreen: 3050327, - seashell: 16774638, - sienna: 10506797, - silver: 12632256, - skyblue: 8900331, - slateblue: 6970061, - slategray: 7372944, - slategrey: 7372944, - snow: 16775930, - springgreen: 65407, - steelblue: 4620980, - tan: 13808780, - teal: 32896, - thistle: 14204888, - tomato: 16737095, - turquoise: 4251856, - violet: 15631086, - wheat: 16113331, - white: 16777215, - whitesmoke: 16119285, - yellow: 16776960, - yellowgreen: 10145074 - }); - d3_rgb_names.forEach(function(key, value) { - d3_rgb_names.set(key, d3_rgbNumber(value)); - }); - function d3_functor(v) { - return typeof v === "function" ? v : function() { - return v; - }; - } - d3.functor = d3_functor; - d3.xhr = d3_xhrType(d3_identity); - function d3_xhrType(response) { - return function(url, mimeType, callback) { - if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, - mimeType = null; - return d3_xhr(url, mimeType, response, callback); - }; - } - function d3_xhr(url, mimeType, response, callback) { - var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; - if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); - "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { - request.readyState > 3 && respond(); - }; - function respond() { - var status = request.status, result; - if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { - try { - result = response.call(xhr, request); - } catch (e) { - dispatch.error.call(xhr, e); - return; - } - dispatch.load.call(xhr, result); - } else { - dispatch.error.call(xhr, request); - } - } - request.onprogress = function(event) { - var o = d3.event; - d3.event = event; - try { - dispatch.progress.call(xhr, request); - } finally { - d3.event = o; - } - }; - xhr.header = function(name, value) { - name = (name + "").toLowerCase(); - if (arguments.length < 2) return headers[name]; - if (value == null) delete headers[name]; else headers[name] = value + ""; - return xhr; - }; - xhr.mimeType = function(value) { - if (!arguments.length) return mimeType; - mimeType = value == null ? null : value + ""; - return xhr; - }; - xhr.responseType = function(value) { - if (!arguments.length) return responseType; - responseType = value; - return xhr; - }; - xhr.response = function(value) { - response = value; - return xhr; - }; - [ "get", "post" ].forEach(function(method) { - xhr[method] = function() { - return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); - }; - }); - xhr.send = function(method, data, callback) { - if (arguments.length === 2 && typeof data === "function") callback = data, data = null; - request.open(method, url, true); - if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; - if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); - if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); - if (responseType != null) request.responseType = responseType; - if (callback != null) xhr.on("error", callback).on("load", function(request) { - callback(null, request); - }); - dispatch.beforesend.call(xhr, request); - request.send(data == null ? null : data); - return xhr; - }; - xhr.abort = function() { - request.abort(); - return xhr; - }; - d3.rebind(xhr, dispatch, "on"); - return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); - } - function d3_xhr_fixCallback(callback) { - return callback.length === 1 ? function(error, request) { - callback(error == null ? request : null); - } : callback; - } - function d3_xhrHasResponse(request) { - var type = request.responseType; - return type && type !== "text" ? request.response : request.responseText; - } - d3.dsv = function(delimiter, mimeType) { - var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); - function dsv(url, row, callback) { - if (arguments.length < 3) callback = row, row = null; - var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); - xhr.row = function(_) { - return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; - }; - return xhr; - } - function response(request) { - return dsv.parse(request.responseText); - } - function typedResponse(f) { - return function(request) { - return dsv.parse(request.responseText, f); - }; - } - dsv.parse = function(text, f) { - var o; - return dsv.parseRows(text, function(row, i) { - if (o) return o(row, i - 1); - var a = new Function("d", "return {" + row.map(function(name, i) { - return JSON.stringify(name) + ": d[" + i + "]"; - }).join(",") + "}"); - o = f ? function(row, i) { - return f(a(row), i); - } : a; - }); - }; - dsv.parseRows = function(text, f) { - var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; - function token() { - if (I >= N) return EOF; - if (eol) return eol = false, EOL; - var j = I; - if (text.charCodeAt(j) === 34) { - var i = j; - while (i++ < N) { - if (text.charCodeAt(i) === 34) { - if (text.charCodeAt(i + 1) !== 34) break; - ++i; - } - } - I = i + 2; - var c = text.charCodeAt(i + 1); - if (c === 13) { - eol = true; - if (text.charCodeAt(i + 2) === 10) ++I; - } else if (c === 10) { - eol = true; - } - return text.slice(j + 1, i).replace(/""/g, '"'); - } - while (I < N) { - var c = text.charCodeAt(I++), k = 1; - if (c === 10) eol = true; else if (c === 13) { - eol = true; - if (text.charCodeAt(I) === 10) ++I, ++k; - } else if (c !== delimiterCode) continue; - return text.slice(j, I - k); - } - return text.slice(j); - } - while ((t = token()) !== EOF) { - var a = []; - while (t !== EOL && t !== EOF) { - a.push(t); - t = token(); - } - if (f && (a = f(a, n++)) == null) continue; - rows.push(a); - } - return rows; - }; - dsv.format = function(rows) { - if (Array.isArray(rows[0])) return dsv.formatRows(rows); - var fieldSet = new d3_Set(), fields = []; - rows.forEach(function(row) { - for (var field in row) { - if (!fieldSet.has(field)) { - fields.push(fieldSet.add(field)); - } - } - }); - return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { - return fields.map(function(field) { - return formatValue(row[field]); - }).join(delimiter); - })).join("\n"); - }; - dsv.formatRows = function(rows) { - return rows.map(formatRow).join("\n"); - }; - function formatRow(row) { - return row.map(formatValue).join(delimiter); - } - function formatValue(text) { - return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; - } - return dsv; - }; - d3.csv = d3.dsv(",", "text/csv"); - d3.tsv = d3.dsv(" ", "text/tab-separated-values"); - var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { - setTimeout(callback, 17); - }; - d3.timer = function() { - d3_timer.apply(this, arguments); - }; - function d3_timer(callback, delay, then) { - var n = arguments.length; - if (n < 2) delay = 0; - if (n < 3) then = Date.now(); - var time = then + delay, timer = { - c: callback, - t: time, - n: null - }; - if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; - d3_timer_queueTail = timer; - if (!d3_timer_interval) { - d3_timer_timeout = clearTimeout(d3_timer_timeout); - d3_timer_interval = 1; - d3_timer_frame(d3_timer_step); - } - return timer; - } - function d3_timer_step() { - var now = d3_timer_mark(), delay = d3_timer_sweep() - now; - if (delay > 24) { - if (isFinite(delay)) { - clearTimeout(d3_timer_timeout); - d3_timer_timeout = setTimeout(d3_timer_step, delay); - } - d3_timer_interval = 0; - } else { - d3_timer_interval = 1; - d3_timer_frame(d3_timer_step); - } - } - d3.timer.flush = function() { - d3_timer_mark(); - d3_timer_sweep(); - }; - function d3_timer_mark() { - var now = Date.now(), timer = d3_timer_queueHead; - while (timer) { - if (now >= timer.t && timer.c(now - timer.t)) timer.c = null; - timer = timer.n; - } - return now; - } - function d3_timer_sweep() { - var t0, t1 = d3_timer_queueHead, time = Infinity; - while (t1) { - if (t1.c) { - if (t1.t < time) time = t1.t; - t1 = (t0 = t1).n; - } else { - t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; - } - } - d3_timer_queueTail = t0; - return time; - } - function d3_format_precision(x, p) { - return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); - } - d3.round = function(x, n) { - return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); - }; - var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); - d3.formatPrefix = function(value, precision) { - var i = 0; - if (value = +value) { - if (value < 0) value *= -1; - if (precision) value = d3.round(value, d3_format_precision(value, precision)); - i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); - i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); - } - return d3_formatPrefixes[8 + i / 3]; - }; - function d3_formatPrefix(d, i) { - var k = Math.pow(10, abs(8 - i) * 3); - return { - scale: i > 8 ? function(d) { - return d / k; - } : function(d) { - return d * k; - }, - symbol: d - }; - } - function d3_locale_numberFormat(locale) { - var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) { - var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0; - while (i > 0 && g > 0) { - if (length + g + 1 > width) g = Math.max(1, width - length); - t.push(value.substring(i -= g, i + g)); - if ((length += g + 1) > width) break; - g = locale_grouping[j = (j + 1) % locale_grouping.length]; - } - return t.reverse().join(locale_thousands); - } : d3_identity; - return function(specifier) { - var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true; - if (precision) precision = +precision.substring(1); - if (zfill || fill === "0" && align === "=") { - zfill = fill = "0"; - align = "="; - } - switch (type) { - case "n": - comma = true; - type = "g"; - break; - - case "%": - scale = 100; - suffix = "%"; - type = "f"; - break; - - case "p": - scale = 100; - suffix = "%"; - type = "r"; - break; - - case "b": - case "o": - case "x": - case "X": - if (symbol === "#") prefix = "0" + type.toLowerCase(); - - case "c": - exponent = false; - - case "d": - integer = true; - precision = 0; - break; - - case "s": - scale = -1; - type = "r"; - break; - } - if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; - if (type == "r" && !precision) type = "g"; - if (precision != null) { - if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); - } - type = d3_format_types.get(type) || d3_format_typeDefault; - var zcomma = zfill && comma; - return function(value) { - var fullSuffix = suffix; - if (integer && value % 1) return ""; - var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign; - if (scale < 0) { - var unit = d3.formatPrefix(value, precision); - value = unit.scale(value); - fullSuffix = unit.symbol + suffix; - } else { - value *= scale; - } - value = type(value, precision); - var i = value.lastIndexOf("."), before, after; - if (i < 0) { - var j = exponent ? value.lastIndexOf("e") : -1; - if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j); - } else { - before = value.substring(0, i); - after = locale_decimal + value.substring(i + 1); - } - if (!zfill && comma) before = formatGroup(before, Infinity); - var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; - if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity); - negative += prefix; - value = before + after; - return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; - }; - }; - } - var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; - var d3_format_types = d3.map({ - b: function(x) { - return x.toString(2); - }, - c: function(x) { - return String.fromCharCode(x); - }, - o: function(x) { - return x.toString(8); - }, - x: function(x) { - return x.toString(16); - }, - X: function(x) { - return x.toString(16).toUpperCase(); - }, - g: function(x, p) { - return x.toPrecision(p); - }, - e: function(x, p) { - return x.toExponential(p); - }, - f: function(x, p) { - return x.toFixed(p); - }, - r: function(x, p) { - return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); - } - }); - function d3_format_typeDefault(x) { - return x + ""; - } - var d3_time = d3.time = {}, d3_date = Date; - function d3_date_utc() { - this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); - } - d3_date_utc.prototype = { - getDate: function() { - return this._.getUTCDate(); - }, - getDay: function() { - return this._.getUTCDay(); - }, - getFullYear: function() { - return this._.getUTCFullYear(); - }, - getHours: function() { - return this._.getUTCHours(); - }, - getMilliseconds: function() { - return this._.getUTCMilliseconds(); - }, - getMinutes: function() { - return this._.getUTCMinutes(); - }, - getMonth: function() { - return this._.getUTCMonth(); - }, - getSeconds: function() { - return this._.getUTCSeconds(); - }, - getTime: function() { - return this._.getTime(); - }, - getTimezoneOffset: function() { - return 0; - }, - valueOf: function() { - return this._.valueOf(); - }, - setDate: function() { - d3_time_prototype.setUTCDate.apply(this._, arguments); - }, - setDay: function() { - d3_time_prototype.setUTCDay.apply(this._, arguments); - }, - setFullYear: function() { - d3_time_prototype.setUTCFullYear.apply(this._, arguments); - }, - setHours: function() { - d3_time_prototype.setUTCHours.apply(this._, arguments); - }, - setMilliseconds: function() { - d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); - }, - setMinutes: function() { - d3_time_prototype.setUTCMinutes.apply(this._, arguments); - }, - setMonth: function() { - d3_time_prototype.setUTCMonth.apply(this._, arguments); - }, - setSeconds: function() { - d3_time_prototype.setUTCSeconds.apply(this._, arguments); - }, - setTime: function() { - d3_time_prototype.setTime.apply(this._, arguments); - } - }; - var d3_time_prototype = Date.prototype; - function d3_time_interval(local, step, number) { - function round(date) { - var d0 = local(date), d1 = offset(d0, 1); - return date - d0 < d1 - date ? d0 : d1; - } - function ceil(date) { - step(date = local(new d3_date(date - 1)), 1); - return date; - } - function offset(date, k) { - step(date = new d3_date(+date), k); - return date; - } - function range(t0, t1, dt) { - var time = ceil(t0), times = []; - if (dt > 1) { - while (time < t1) { - if (!(number(time) % dt)) times.push(new Date(+time)); - step(time, 1); - } - } else { - while (time < t1) times.push(new Date(+time)), step(time, 1); - } - return times; - } - function range_utc(t0, t1, dt) { - try { - d3_date = d3_date_utc; - var utc = new d3_date_utc(); - utc._ = t0; - return range(utc, t1, dt); - } finally { - d3_date = Date; - } - } - local.floor = local; - local.round = round; - local.ceil = ceil; - local.offset = offset; - local.range = range; - var utc = local.utc = d3_time_interval_utc(local); - utc.floor = utc; - utc.round = d3_time_interval_utc(round); - utc.ceil = d3_time_interval_utc(ceil); - utc.offset = d3_time_interval_utc(offset); - utc.range = range_utc; - return local; - } - function d3_time_interval_utc(method) { - return function(date, k) { - try { - d3_date = d3_date_utc; - var utc = new d3_date_utc(); - utc._ = date; - return method(utc, k)._; - } finally { - d3_date = Date; - } - }; - } - d3_time.year = d3_time_interval(function(date) { - date = d3_time.day(date); - date.setMonth(0, 1); - return date; - }, function(date, offset) { - date.setFullYear(date.getFullYear() + offset); - }, function(date) { - return date.getFullYear(); - }); - d3_time.years = d3_time.year.range; - d3_time.years.utc = d3_time.year.utc.range; - d3_time.day = d3_time_interval(function(date) { - var day = new d3_date(2e3, 0); - day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); - return day; - }, function(date, offset) { - date.setDate(date.getDate() + offset); - }, function(date) { - return date.getDate() - 1; - }); - d3_time.days = d3_time.day.range; - d3_time.days.utc = d3_time.day.utc.range; - d3_time.dayOfYear = function(date) { - var year = d3_time.year(date); - return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); - }; - [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { - i = 7 - i; - var interval = d3_time[day] = d3_time_interval(function(date) { - (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); - return date; - }, function(date, offset) { - date.setDate(date.getDate() + Math.floor(offset) * 7); - }, function(date) { - var day = d3_time.year(date).getDay(); - return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); - }); - d3_time[day + "s"] = interval.range; - d3_time[day + "s"].utc = interval.utc.range; - d3_time[day + "OfYear"] = function(date) { - var day = d3_time.year(date).getDay(); - return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); - }; - }); - d3_time.week = d3_time.sunday; - d3_time.weeks = d3_time.sunday.range; - d3_time.weeks.utc = d3_time.sunday.utc.range; - d3_time.weekOfYear = d3_time.sundayOfYear; - function d3_locale_timeFormat(locale) { - var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; - function d3_time_format(template) { - var n = template.length; - function format(date) { - var string = [], i = -1, j = 0, c, p, f; - while (++i < n) { - if (template.charCodeAt(i) === 37) { - string.push(template.slice(j, i)); - if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); - if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); - string.push(c); - j = i + 1; - } - } - string.push(template.slice(j, i)); - return string.join(""); - } - format.parse = function(string) { - var d = { - y: 1900, - m: 0, - d: 1, - H: 0, - M: 0, - S: 0, - L: 0, - Z: null - }, i = d3_time_parse(d, template, string, 0); - if (i != string.length) return null; - if ("p" in d) d.H = d.H % 12 + d.p * 12; - var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); - if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) { - if (!("w" in d)) d.w = "W" in d ? 1 : 0; - date.setFullYear(d.y, 0, 1); - date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); - } else date.setFullYear(d.y, d.m, d.d); - date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L); - return localZ ? date._ : date; - }; - format.toString = function() { - return template; - }; - return format; - } - function d3_time_parse(date, template, string, j) { - var c, p, t, i = 0, n = template.length, m = string.length; - while (i < n) { - if (j >= m) return -1; - c = template.charCodeAt(i++); - if (c === 37) { - t = template.charAt(i++); - p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; - if (!p || (j = p(date, string, j)) < 0) return -1; - } else if (c != string.charCodeAt(j++)) { - return -1; - } - } - return j; - } - d3_time_format.utc = function(template) { - var local = d3_time_format(template); - function format(date) { - try { - d3_date = d3_date_utc; - var utc = new d3_date(); - utc._ = date; - return local(utc); - } finally { - d3_date = Date; - } - } - format.parse = function(string) { - try { - d3_date = d3_date_utc; - var date = local.parse(string); - return date && date._; - } finally { - d3_date = Date; - } - }; - format.toString = local.toString; - return format; - }; - d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; - var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); - locale_periods.forEach(function(p, i) { - d3_time_periodLookup.set(p.toLowerCase(), i); - }); - var d3_time_formats = { - a: function(d) { - return locale_shortDays[d.getDay()]; - }, - A: function(d) { - return locale_days[d.getDay()]; - }, - b: function(d) { - return locale_shortMonths[d.getMonth()]; - }, - B: function(d) { - return locale_months[d.getMonth()]; - }, - c: d3_time_format(locale_dateTime), - d: function(d, p) { - return d3_time_formatPad(d.getDate(), p, 2); - }, - e: function(d, p) { - return d3_time_formatPad(d.getDate(), p, 2); - }, - H: function(d, p) { - return d3_time_formatPad(d.getHours(), p, 2); - }, - I: function(d, p) { - return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); - }, - j: function(d, p) { - return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); - }, - L: function(d, p) { - return d3_time_formatPad(d.getMilliseconds(), p, 3); - }, - m: function(d, p) { - return d3_time_formatPad(d.getMonth() + 1, p, 2); - }, - M: function(d, p) { - return d3_time_formatPad(d.getMinutes(), p, 2); - }, - p: function(d) { - return locale_periods[+(d.getHours() >= 12)]; - }, - S: function(d, p) { - return d3_time_formatPad(d.getSeconds(), p, 2); - }, - U: function(d, p) { - return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); - }, - w: function(d) { - return d.getDay(); - }, - W: function(d, p) { - return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); - }, - x: d3_time_format(locale_date), - X: d3_time_format(locale_time), - y: function(d, p) { - return d3_time_formatPad(d.getFullYear() % 100, p, 2); - }, - Y: function(d, p) { - return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); - }, - Z: d3_time_zone, - "%": function() { - return "%"; - } - }; - var d3_time_parsers = { - a: d3_time_parseWeekdayAbbrev, - A: d3_time_parseWeekday, - b: d3_time_parseMonthAbbrev, - B: d3_time_parseMonth, - c: d3_time_parseLocaleFull, - d: d3_time_parseDay, - e: d3_time_parseDay, - H: d3_time_parseHour24, - I: d3_time_parseHour24, - j: d3_time_parseDayOfYear, - L: d3_time_parseMilliseconds, - m: d3_time_parseMonthNumber, - M: d3_time_parseMinutes, - p: d3_time_parseAmPm, - S: d3_time_parseSeconds, - U: d3_time_parseWeekNumberSunday, - w: d3_time_parseWeekdayNumber, - W: d3_time_parseWeekNumberMonday, - x: d3_time_parseLocaleDate, - X: d3_time_parseLocaleTime, - y: d3_time_parseYear, - Y: d3_time_parseFullYear, - Z: d3_time_parseZone, - "%": d3_time_parseLiteralPercent - }; - function d3_time_parseWeekdayAbbrev(date, string, i) { - d3_time_dayAbbrevRe.lastIndex = 0; - var n = d3_time_dayAbbrevRe.exec(string.slice(i)); - return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseWeekday(date, string, i) { - d3_time_dayRe.lastIndex = 0; - var n = d3_time_dayRe.exec(string.slice(i)); - return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseMonthAbbrev(date, string, i) { - d3_time_monthAbbrevRe.lastIndex = 0; - var n = d3_time_monthAbbrevRe.exec(string.slice(i)); - return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseMonth(date, string, i) { - d3_time_monthRe.lastIndex = 0; - var n = d3_time_monthRe.exec(string.slice(i)); - return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseLocaleFull(date, string, i) { - return d3_time_parse(date, d3_time_formats.c.toString(), string, i); - } - function d3_time_parseLocaleDate(date, string, i) { - return d3_time_parse(date, d3_time_formats.x.toString(), string, i); - } - function d3_time_parseLocaleTime(date, string, i) { - return d3_time_parse(date, d3_time_formats.X.toString(), string, i); - } - function d3_time_parseAmPm(date, string, i) { - var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase()); - return n == null ? -1 : (date.p = n, i); - } - return d3_time_format; - } - var d3_time_formatPads = { - "-": "", - _: " ", - "0": "0" - }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; - function d3_time_formatPad(value, fill, width) { - var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; - return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); - } - function d3_time_formatRe(names) { - return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); - } - function d3_time_formatLookup(names) { - var map = new d3_Map(), i = -1, n = names.length; - while (++i < n) map.set(names[i].toLowerCase(), i); - return map; - } - function d3_time_parseWeekdayNumber(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 1)); - return n ? (date.w = +n[0], i + n[0].length) : -1; - } - function d3_time_parseWeekNumberSunday(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i)); - return n ? (date.U = +n[0], i + n[0].length) : -1; - } - function d3_time_parseWeekNumberMonday(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i)); - return n ? (date.W = +n[0], i + n[0].length) : -1; - } - function d3_time_parseFullYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 4)); - return n ? (date.y = +n[0], i + n[0].length) : -1; - } - function d3_time_parseYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 2)); - return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; - } - function d3_time_parseZone(date, string, i) { - return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string, - i + 5) : -1; - } - function d3_time_expandYear(d) { - return d + (d > 68 ? 1900 : 2e3); - } - function d3_time_parseMonthNumber(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 2)); - return n ? (date.m = n[0] - 1, i + n[0].length) : -1; - } - function d3_time_parseDay(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 2)); - return n ? (date.d = +n[0], i + n[0].length) : -1; - } - function d3_time_parseDayOfYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 3)); - return n ? (date.j = +n[0], i + n[0].length) : -1; - } - function d3_time_parseHour24(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 2)); - return n ? (date.H = +n[0], i + n[0].length) : -1; - } - function d3_time_parseMinutes(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 2)); - return n ? (date.M = +n[0], i + n[0].length) : -1; - } - function d3_time_parseSeconds(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 2)); - return n ? (date.S = +n[0], i + n[0].length) : -1; - } - function d3_time_parseMilliseconds(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.slice(i, i + 3)); - return n ? (date.L = +n[0], i + n[0].length) : -1; - } - function d3_time_zone(d) { - var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60; - return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); - } - function d3_time_parseLiteralPercent(date, string, i) { - d3_time_percentRe.lastIndex = 0; - var n = d3_time_percentRe.exec(string.slice(i, i + 1)); - return n ? i + n[0].length : -1; - } - function d3_time_formatMulti(formats) { - var n = formats.length, i = -1; - while (++i < n) formats[i][0] = this(formats[i][0]); - return function(date) { - var i = 0, f = formats[i]; - while (!f[1](date)) f = formats[++i]; - return f[0](date); - }; - } - d3.locale = function(locale) { - return { - numberFormat: d3_locale_numberFormat(locale), - timeFormat: d3_locale_timeFormat(locale) - }; - }; - var d3_locale_enUS = d3.locale({ - decimal: ".", - thousands: ",", - grouping: [ 3 ], - currency: [ "$", "" ], - dateTime: "%a %b %e %X %Y", - date: "%m/%d/%Y", - time: "%H:%M:%S", - periods: [ "AM", "PM" ], - days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], - shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], - months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], - shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] - }); - d3.format = d3_locale_enUS.numberFormat; - d3.geo = {}; - function d3_adder() {} - d3_adder.prototype = { - s: 0, - t: 0, - add: function(y) { - d3_adderSum(y, this.t, d3_adderTemp); - d3_adderSum(d3_adderTemp.s, this.s, this); - if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; - }, - reset: function() { - this.s = this.t = 0; - }, - valueOf: function() { - return this.s; - } - }; - var d3_adderTemp = new d3_adder(); - function d3_adderSum(a, b, o) { - var x = o.s = a + b, bv = x - a, av = x - bv; - o.t = a - av + (b - bv); - } - d3.geo.stream = function(object, listener) { - if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { - d3_geo_streamObjectType[object.type](object, listener); - } else { - d3_geo_streamGeometry(object, listener); - } - }; - function d3_geo_streamGeometry(geometry, listener) { - if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { - d3_geo_streamGeometryType[geometry.type](geometry, listener); - } - } - var d3_geo_streamObjectType = { - Feature: function(feature, listener) { - d3_geo_streamGeometry(feature.geometry, listener); - }, - FeatureCollection: function(object, listener) { - var features = object.features, i = -1, n = features.length; - while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); - } - }; - var d3_geo_streamGeometryType = { - Sphere: function(object, listener) { - listener.sphere(); - }, - Point: function(object, listener) { - object = object.coordinates; - listener.point(object[0], object[1], object[2]); - }, - MultiPoint: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); - }, - LineString: function(object, listener) { - d3_geo_streamLine(object.coordinates, listener, 0); - }, - MultiLineString: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); - }, - Polygon: function(object, listener) { - d3_geo_streamPolygon(object.coordinates, listener); - }, - MultiPolygon: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); - }, - GeometryCollection: function(object, listener) { - var geometries = object.geometries, i = -1, n = geometries.length; - while (++i < n) d3_geo_streamGeometry(geometries[i], listener); - } - }; - function d3_geo_streamLine(coordinates, listener, closed) { - var i = -1, n = coordinates.length - closed, coordinate; - listener.lineStart(); - while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); - listener.lineEnd(); - } - function d3_geo_streamPolygon(coordinates, listener) { - var i = -1, n = coordinates.length; - listener.polygonStart(); - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); - listener.polygonEnd(); - } - d3.geo.area = function(object) { - d3_geo_areaSum = 0; - d3.geo.stream(object, d3_geo_area); - return d3_geo_areaSum; - }; - var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); - var d3_geo_area = { - sphere: function() { - d3_geo_areaSum += 4 * π; - }, - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: function() { - d3_geo_areaRingSum.reset(); - d3_geo_area.lineStart = d3_geo_areaRingStart; - }, - polygonEnd: function() { - var area = 2 * d3_geo_areaRingSum; - d3_geo_areaSum += area < 0 ? 4 * π + area : area; - d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; - } - }; - function d3_geo_areaRingStart() { - var λ00, φ00, λ0, cosφ0, sinφ0; - d3_geo_area.point = function(λ, φ) { - d3_geo_area.point = nextPoint; - λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), - sinφ0 = Math.sin(φ); - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - φ = φ * d3_radians / 2 + π / 4; - var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); - d3_geo_areaRingSum.add(Math.atan2(v, u)); - λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; - } - d3_geo_area.lineEnd = function() { - nextPoint(λ00, φ00); - }; - } - function d3_geo_cartesian(spherical) { - var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); - return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; - } - function d3_geo_cartesianDot(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; - } - function d3_geo_cartesianCross(a, b) { - return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; - } - function d3_geo_cartesianAdd(a, b) { - a[0] += b[0]; - a[1] += b[1]; - a[2] += b[2]; - } - function d3_geo_cartesianScale(vector, k) { - return [ vector[0] * k, vector[1] * k, vector[2] * k ]; - } - function d3_geo_cartesianNormalize(d) { - var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); - d[0] /= l; - d[1] /= l; - d[2] /= l; - } - function d3_geo_spherical(cartesian) { - return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; - } - function d3_geo_sphericalEqual(a, b) { - return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; - } - d3.geo.bounds = function() { - var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; - var bound = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - bound.point = ringPoint; - bound.lineStart = ringStart; - bound.lineEnd = ringEnd; - dλSum = 0; - d3_geo_area.polygonStart(); - }, - polygonEnd: function() { - d3_geo_area.polygonEnd(); - bound.point = point; - bound.lineStart = lineStart; - bound.lineEnd = lineEnd; - if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; - range[0] = λ0, range[1] = λ1; - } - }; - function point(λ, φ) { - ranges.push(range = [ λ0 = λ, λ1 = λ ]); - if (φ < φ0) φ0 = φ; - if (φ > φ1) φ1 = φ; - } - function linePoint(λ, φ) { - var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); - if (p0) { - var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); - d3_geo_cartesianNormalize(inflection); - inflection = d3_geo_spherical(inflection); - var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; - if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { - var φi = inflection[1] * d3_degrees; - if (φi > φ1) φ1 = φi; - } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { - var φi = -inflection[1] * d3_degrees; - if (φi < φ0) φ0 = φi; - } else { - if (φ < φ0) φ0 = φ; - if (φ > φ1) φ1 = φ; - } - if (antimeridian) { - if (λ < λ_) { - if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; - } else { - if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; - } - } else { - if (λ1 >= λ0) { - if (λ < λ0) λ0 = λ; - if (λ > λ1) λ1 = λ; - } else { - if (λ > λ_) { - if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; - } else { - if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; - } - } - } - } else { - point(λ, φ); - } - p0 = p, λ_ = λ; - } - function lineStart() { - bound.point = linePoint; - } - function lineEnd() { - range[0] = λ0, range[1] = λ1; - bound.point = point; - p0 = null; - } - function ringPoint(λ, φ) { - if (p0) { - var dλ = λ - λ_; - dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; - } else λ__ = λ, φ__ = φ; - d3_geo_area.point(λ, φ); - linePoint(λ, φ); - } - function ringStart() { - d3_geo_area.lineStart(); - } - function ringEnd() { - ringPoint(λ__, φ__); - d3_geo_area.lineEnd(); - if (abs(dλSum) > ε) λ0 = -(λ1 = 180); - range[0] = λ0, range[1] = λ1; - p0 = null; - } - function angle(λ0, λ1) { - return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; - } - function compareRanges(a, b) { - return a[0] - b[0]; - } - function withinRange(x, range) { - return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; - } - return function(feature) { - φ1 = λ1 = -(λ0 = φ0 = Infinity); - ranges = []; - d3.geo.stream(feature, bound); - var n = ranges.length; - if (n) { - ranges.sort(compareRanges); - for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { - b = ranges[i]; - if (withinRange(b[0], a) || withinRange(b[1], a)) { - if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; - if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; - } else { - merged.push(a = b); - } - } - var best = -Infinity, dλ; - for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { - b = merged[i]; - if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; - } - } - ranges = range = null; - return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; - }; - }(); - d3.geo.centroid = function(object) { - d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; - d3.geo.stream(object, d3_geo_centroid); - var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; - if (m < ε2) { - x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; - if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; - m = x * x + y * y + z * z; - if (m < ε2) return [ NaN, NaN ]; - } - return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; - }; - var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; - var d3_geo_centroid = { - sphere: d3_noop, - point: d3_geo_centroidPoint, - lineStart: d3_geo_centroidLineStart, - lineEnd: d3_geo_centroidLineEnd, - polygonStart: function() { - d3_geo_centroid.lineStart = d3_geo_centroidRingStart; - }, - polygonEnd: function() { - d3_geo_centroid.lineStart = d3_geo_centroidLineStart; - } - }; - function d3_geo_centroidPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); - } - function d3_geo_centroidPointXYZ(x, y, z) { - ++d3_geo_centroidW0; - d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; - d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; - d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; - } - function d3_geo_centroidLineStart() { - var x0, y0, z0; - d3_geo_centroid.point = function(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroid.point = nextPoint; - d3_geo_centroidPointXYZ(x0, y0, z0); - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); - d3_geo_centroidW1 += w; - d3_geo_centroidX1 += w * (x0 + (x0 = x)); - d3_geo_centroidY1 += w * (y0 + (y0 = y)); - d3_geo_centroidZ1 += w * (z0 + (z0 = z)); - d3_geo_centroidPointXYZ(x0, y0, z0); - } - } - function d3_geo_centroidLineEnd() { - d3_geo_centroid.point = d3_geo_centroidPoint; - } - function d3_geo_centroidRingStart() { - var λ00, φ00, x0, y0, z0; - d3_geo_centroid.point = function(λ, φ) { - λ00 = λ, φ00 = φ; - d3_geo_centroid.point = nextPoint; - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroidPointXYZ(x0, y0, z0); - }; - d3_geo_centroid.lineEnd = function() { - nextPoint(λ00, φ00); - d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; - d3_geo_centroid.point = d3_geo_centroidPoint; - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); - d3_geo_centroidX2 += v * cx; - d3_geo_centroidY2 += v * cy; - d3_geo_centroidZ2 += v * cz; - d3_geo_centroidW1 += w; - d3_geo_centroidX1 += w * (x0 + (x0 = x)); - d3_geo_centroidY1 += w * (y0 + (y0 = y)); - d3_geo_centroidZ1 += w * (z0 + (z0 = z)); - d3_geo_centroidPointXYZ(x0, y0, z0); - } - } - function d3_geo_compose(a, b) { - function compose(x, y) { - return x = a(x, y), b(x[0], x[1]); - } - if (a.invert && b.invert) compose.invert = function(x, y) { - return x = b.invert(x, y), x && a.invert(x[0], x[1]); - }; - return compose; - } - function d3_true() { - return true; - } - function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { - var subject = [], clip = []; - segments.forEach(function(segment) { - if ((n = segment.length - 1) <= 0) return; - var n, p0 = segment[0], p1 = segment[n]; - if (d3_geo_sphericalEqual(p0, p1)) { - listener.lineStart(); - for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); - listener.lineEnd(); - return; - } - var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); - a.o = b; - subject.push(a); - clip.push(b); - a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); - b = new d3_geo_clipPolygonIntersection(p1, null, a, true); - a.o = b; - subject.push(a); - clip.push(b); - }); - clip.sort(compare); - d3_geo_clipPolygonLinkCircular(subject); - d3_geo_clipPolygonLinkCircular(clip); - if (!subject.length) return; - for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { - clip[i].e = entry = !entry; - } - var start = subject[0], points, point; - while (1) { - var current = start, isSubject = true; - while (current.v) if ((current = current.n) === start) return; - points = current.z; - listener.lineStart(); - do { - current.v = current.o.v = true; - if (current.e) { - if (isSubject) { - for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.n.x, 1, listener); - } - current = current.n; - } else { - if (isSubject) { - points = current.p.z; - for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.p.x, -1, listener); - } - current = current.p; - } - current = current.o; - points = current.z; - isSubject = !isSubject; - } while (!current.v); - listener.lineEnd(); - } - } - function d3_geo_clipPolygonLinkCircular(array) { - if (!(n = array.length)) return; - var n, i = 0, a = array[0], b; - while (++i < n) { - a.n = b = array[i]; - b.p = a; - a = b; - } - a.n = b = array[0]; - b.p = a; - } - function d3_geo_clipPolygonIntersection(point, points, other, entry) { - this.x = point; - this.z = points; - this.o = other; - this.e = entry; - this.v = false; - this.n = this.p = null; - } - function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { - return function(rotate, listener) { - var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - clip.point = pointRing; - clip.lineStart = ringStart; - clip.lineEnd = ringEnd; - segments = []; - polygon = []; - }, - polygonEnd: function() { - clip.point = point; - clip.lineStart = lineStart; - clip.lineEnd = lineEnd; - segments = d3.merge(segments); - var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); - if (segments.length) { - if (!polygonStarted) listener.polygonStart(), polygonStarted = true; - d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); - } else if (clipStartInside) { - if (!polygonStarted) listener.polygonStart(), polygonStarted = true; - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - } - if (polygonStarted) listener.polygonEnd(), polygonStarted = false; - segments = polygon = null; - }, - sphere: function() { - listener.polygonStart(); - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - listener.polygonEnd(); - } - }; - function point(λ, φ) { - var point = rotate(λ, φ); - if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); - } - function pointLine(λ, φ) { - var point = rotate(λ, φ); - line.point(point[0], point[1]); - } - function lineStart() { - clip.point = pointLine; - line.lineStart(); - } - function lineEnd() { - clip.point = point; - line.lineEnd(); - } - var segments; - var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; - function pointRing(λ, φ) { - ring.push([ λ, φ ]); - var point = rotate(λ, φ); - ringListener.point(point[0], point[1]); - } - function ringStart() { - ringListener.lineStart(); - ring = []; - } - function ringEnd() { - pointRing(ring[0][0], ring[0][1]); - ringListener.lineEnd(); - var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; - ring.pop(); - polygon.push(ring); - ring = null; - if (!n) return; - if (clean & 1) { - segment = ringSegments[0]; - var n = segment.length - 1, i = -1, point; - if (n > 0) { - if (!polygonStarted) listener.polygonStart(), polygonStarted = true; - listener.lineStart(); - while (++i < n) listener.point((point = segment[i])[0], point[1]); - listener.lineEnd(); - } - return; - } - if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); - segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); - } - return clip; - }; - } - function d3_geo_clipSegmentLength1(segment) { - return segment.length > 1; - } - function d3_geo_clipBufferListener() { - var lines = [], line; - return { - lineStart: function() { - lines.push(line = []); - }, - point: function(λ, φ) { - line.push([ λ, φ ]); - }, - lineEnd: d3_noop, - buffer: function() { - var buffer = lines; - lines = []; - line = null; - return buffer; - }, - rejoin: function() { - if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); - } - }; - } - function d3_geo_clipSort(a, b) { - return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); - } - var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); - function d3_geo_clipAntimeridianLine(listener) { - var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; - return { - lineStart: function() { - listener.lineStart(); - clean = 1; - }, - point: function(λ1, φ1) { - var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); - if (abs(dλ - π) < ε) { - listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - listener.point(λ1, φ0); - clean = 0; - } else if (sλ0 !== sλ1 && dλ >= π) { - if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; - if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; - φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - clean = 0; - } - listener.point(λ0 = λ1, φ0 = φ1); - sλ0 = sλ1; - }, - lineEnd: function() { - listener.lineEnd(); - λ0 = φ0 = NaN; - }, - clean: function() { - return 2 - clean; - } - }; - } - function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { - var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); - return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; - } - function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { - var φ; - if (from == null) { - φ = direction * halfπ; - listener.point(-π, φ); - listener.point(0, φ); - listener.point(π, φ); - listener.point(π, 0); - listener.point(π, -φ); - listener.point(0, -φ); - listener.point(-π, -φ); - listener.point(-π, 0); - listener.point(-π, φ); - } else if (abs(from[0] - to[0]) > ε) { - var s = from[0] < to[0] ? π : -π; - φ = direction * s / 2; - listener.point(-s, φ); - listener.point(0, φ); - listener.point(s, φ); - } else { - listener.point(to[0], to[1]); - } - } - function d3_geo_pointInPolygon(point, polygon) { - var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; - d3_geo_areaRingSum.reset(); - for (var i = 0, n = polygon.length; i < n; ++i) { - var ring = polygon[i], m = ring.length; - if (!m) continue; - var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; - while (true) { - if (j === m) j = 0; - point = ring[j]; - var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; - d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); - polarAngle += antimeridian ? dλ + sdλ * τ : dλ; - if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { - var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); - d3_geo_cartesianNormalize(arc); - var intersection = d3_geo_cartesianCross(meridianNormal, arc); - d3_geo_cartesianNormalize(intersection); - var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); - if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { - winding += antimeridian ^ dλ >= 0 ? 1 : -1; - } - } - if (!j++) break; - λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; - } - } - return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ winding & 1; - } - function d3_geo_clipCircle(radius) { - var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); - return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]); - function visible(λ, φ) { - return Math.cos(λ) * Math.cos(φ) > cr; - } - function clipLine(listener) { - var point0, c0, v0, v00, clean; - return { - lineStart: function() { - v00 = v0 = false; - clean = 1; - }, - point: function(λ, φ) { - var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; - if (!point0 && (v00 = v0 = v)) listener.lineStart(); - if (v !== v0) { - point2 = intersect(point0, point1); - if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { - point1[0] += ε; - point1[1] += ε; - v = visible(point1[0], point1[1]); - } - } - if (v !== v0) { - clean = 0; - if (v) { - listener.lineStart(); - point2 = intersect(point1, point0); - listener.point(point2[0], point2[1]); - } else { - point2 = intersect(point0, point1); - listener.point(point2[0], point2[1]); - listener.lineEnd(); - } - point0 = point2; - } else if (notHemisphere && point0 && smallRadius ^ v) { - var t; - if (!(c & c0) && (t = intersect(point1, point0, true))) { - clean = 0; - if (smallRadius) { - listener.lineStart(); - listener.point(t[0][0], t[0][1]); - listener.point(t[1][0], t[1][1]); - listener.lineEnd(); - } else { - listener.point(t[1][0], t[1][1]); - listener.lineEnd(); - listener.lineStart(); - listener.point(t[0][0], t[0][1]); - } - } - } - if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { - listener.point(point1[0], point1[1]); - } - point0 = point1, v0 = v, c0 = c; - }, - lineEnd: function() { - if (v0) listener.lineEnd(); - point0 = null; - }, - clean: function() { - return clean | (v00 && v0) << 1; - } - }; - } - function intersect(a, b, two) { - var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); - var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; - if (!determinant) return !two && a; - var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); - d3_geo_cartesianAdd(A, B); - var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); - if (t2 < 0) return; - var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); - d3_geo_cartesianAdd(q, A); - q = d3_geo_spherical(q); - if (!two) return q; - var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; - if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; - var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; - if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; - if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { - var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); - d3_geo_cartesianAdd(q1, A); - return [ q, d3_geo_spherical(q1) ]; - } - } - function code(λ, φ) { - var r = smallRadius ? radius : π - radius, code = 0; - if (λ < -r) code |= 1; else if (λ > r) code |= 2; - if (φ < -r) code |= 4; else if (φ > r) code |= 8; - return code; - } - } - function d3_geom_clipLine(x0, y0, x1, y1) { - return function(line) { - var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r; - r = x0 - ax; - if (!dx && r > 0) return; - r /= dx; - if (dx < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dx > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - r = x1 - ax; - if (!dx && r < 0) return; - r /= dx; - if (dx < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dx > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - r = y0 - ay; - if (!dy && r > 0) return; - r /= dy; - if (dy < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dy > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - r = y1 - ay; - if (!dy && r < 0) return; - r /= dy; - if (dy < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dy > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - if (t0 > 0) line.a = { - x: ax + t0 * dx, - y: ay + t0 * dy - }; - if (t1 < 1) line.b = { - x: ax + t1 * dx, - y: ay + t1 * dy - }; - return line; - }; - } - var d3_geo_clipExtentMAX = 1e9; - d3.geo.clipExtent = function() { - var x0, y0, x1, y1, stream, clip, clipExtent = { - stream: function(output) { - if (stream) stream.valid = false; - stream = clip(output); - stream.valid = true; - return stream; - }, - extent: function(_) { - if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; - clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); - if (stream) stream.valid = false, stream = null; - return clipExtent; - } - }; - return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); - }; - function d3_geo_clipExtent(x0, y0, x1, y1) { - return function(listener) { - var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - listener = bufferListener; - segments = []; - polygon = []; - clean = true; - }, - polygonEnd: function() { - listener = listener_; - segments = d3.merge(segments); - var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; - if (inside || visible) { - listener.polygonStart(); - if (inside) { - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - } - if (visible) { - d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); - } - listener.polygonEnd(); - } - segments = polygon = ring = null; - } - }; - function insidePolygon(p) { - var wn = 0, n = polygon.length, y = p[1]; - for (var i = 0; i < n; ++i) { - for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { - b = v[j]; - if (a[1] <= y) { - if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; - } else { - if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; - } - a = b; - } - } - return wn !== 0; - } - function interpolate(from, to, direction, listener) { - var a = 0, a1 = 0; - if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { - do { - listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); - } while ((a = (a + direction + 4) % 4) !== a1); - } else { - listener.point(to[0], to[1]); - } - } - function pointVisible(x, y) { - return x0 <= x && x <= x1 && y0 <= y && y <= y1; - } - function point(x, y) { - if (pointVisible(x, y)) listener.point(x, y); - } - var x__, y__, v__, x_, y_, v_, first, clean; - function lineStart() { - clip.point = linePoint; - if (polygon) polygon.push(ring = []); - first = true; - v_ = false; - x_ = y_ = NaN; - } - function lineEnd() { - if (segments) { - linePoint(x__, y__); - if (v__ && v_) bufferListener.rejoin(); - segments.push(bufferListener.buffer()); - } - clip.point = point; - if (v_) listener.lineEnd(); - } - function linePoint(x, y) { - x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); - y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); - var v = pointVisible(x, y); - if (polygon) ring.push([ x, y ]); - if (first) { - x__ = x, y__ = y, v__ = v; - first = false; - if (v) { - listener.lineStart(); - listener.point(x, y); - } - } else { - if (v && v_) listener.point(x, y); else { - var l = { - a: { - x: x_, - y: y_ - }, - b: { - x: x, - y: y - } - }; - if (clipLine(l)) { - if (!v_) { - listener.lineStart(); - listener.point(l.a.x, l.a.y); - } - listener.point(l.b.x, l.b.y); - if (!v) listener.lineEnd(); - clean = false; - } else if (v) { - listener.lineStart(); - listener.point(x, y); - clean = false; - } - } - } - x_ = x, y_ = y, v_ = v; - } - return clip; - }; - function corner(p, direction) { - return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; - } - function compare(a, b) { - return comparePoints(a.x, b.x); - } - function comparePoints(a, b) { - var ca = corner(a, 1), cb = corner(b, 1); - return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; - } - } - function d3_geo_conic(projectAt) { - var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); - p.parallels = function(_) { - if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ]; - return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); - }; - return p; - } - function d3_geo_conicEqualArea(φ0, φ1) { - var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; - function forward(λ, φ) { - var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; - return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = ρ0 - y; - return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; - }; - return forward; - } - (d3.geo.conicEqualArea = function() { - return d3_geo_conic(d3_geo_conicEqualArea); - }).raw = d3_geo_conicEqualArea; - d3.geo.albers = function() { - return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); - }; - d3.geo.albersUsa = function() { - var lower48 = d3.geo.albers(); - var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); - var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); - var point, pointStream = { - point: function(x, y) { - point = [ x, y ]; - } - }, lower48Point, alaskaPoint, hawaiiPoint; - function albersUsa(coordinates) { - var x = coordinates[0], y = coordinates[1]; - point = null; - (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); - return point; - } - albersUsa.invert = function(coordinates) { - var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k; - return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); - }; - albersUsa.stream = function(stream) { - var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); - return { - point: function(x, y) { - lower48Stream.point(x, y); - alaskaStream.point(x, y); - hawaiiStream.point(x, y); - }, - sphere: function() { - lower48Stream.sphere(); - alaskaStream.sphere(); - hawaiiStream.sphere(); - }, - lineStart: function() { - lower48Stream.lineStart(); - alaskaStream.lineStart(); - hawaiiStream.lineStart(); - }, - lineEnd: function() { - lower48Stream.lineEnd(); - alaskaStream.lineEnd(); - hawaiiStream.lineEnd(); - }, - polygonStart: function() { - lower48Stream.polygonStart(); - alaskaStream.polygonStart(); - hawaiiStream.polygonStart(); - }, - polygonEnd: function() { - lower48Stream.polygonEnd(); - alaskaStream.polygonEnd(); - hawaiiStream.polygonEnd(); - } - }; - }; - albersUsa.precision = function(_) { - if (!arguments.length) return lower48.precision(); - lower48.precision(_); - alaska.precision(_); - hawaii.precision(_); - return albersUsa; - }; - albersUsa.scale = function(_) { - if (!arguments.length) return lower48.scale(); - lower48.scale(_); - alaska.scale(_ * .35); - hawaii.scale(_); - return albersUsa.translate(lower48.translate()); - }; - albersUsa.translate = function(_) { - if (!arguments.length) return lower48.translate(); - var k = lower48.scale(), x = +_[0], y = +_[1]; - lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; - alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; - hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; - return albersUsa; - }; - return albersUsa.scale(1070); - }; - var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: function() { - d3_geo_pathAreaPolygon = 0; - d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; - }, - polygonEnd: function() { - d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; - d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); - } - }; - function d3_geo_pathAreaRingStart() { - var x00, y00, x0, y0; - d3_geo_pathArea.point = function(x, y) { - d3_geo_pathArea.point = nextPoint; - x00 = x0 = x, y00 = y0 = y; - }; - function nextPoint(x, y) { - d3_geo_pathAreaPolygon += y0 * x - x0 * y; - x0 = x, y0 = y; - } - d3_geo_pathArea.lineEnd = function() { - nextPoint(x00, y00); - }; - } - var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; - var d3_geo_pathBounds = { - point: d3_geo_pathBoundsPoint, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: d3_noop, - polygonEnd: d3_noop - }; - function d3_geo_pathBoundsPoint(x, y) { - if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; - if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; - if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; - if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; - } - function d3_geo_pathBuffer() { - var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; - var stream = { - point: point, - lineStart: function() { - stream.point = pointLineStart; - }, - lineEnd: lineEnd, - polygonStart: function() { - stream.lineEnd = lineEndPolygon; - }, - polygonEnd: function() { - stream.lineEnd = lineEnd; - stream.point = point; - }, - pointRadius: function(_) { - pointCircle = d3_geo_pathBufferCircle(_); - return stream; - }, - result: function() { - if (buffer.length) { - var result = buffer.join(""); - buffer = []; - return result; - } - } - }; - function point(x, y) { - buffer.push("M", x, ",", y, pointCircle); - } - function pointLineStart(x, y) { - buffer.push("M", x, ",", y); - stream.point = pointLine; - } - function pointLine(x, y) { - buffer.push("L", x, ",", y); - } - function lineEnd() { - stream.point = point; - } - function lineEndPolygon() { - buffer.push("Z"); - } - return stream; - } - function d3_geo_pathBufferCircle(radius) { - return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; - } - var d3_geo_pathCentroid = { - point: d3_geo_pathCentroidPoint, - lineStart: d3_geo_pathCentroidLineStart, - lineEnd: d3_geo_pathCentroidLineEnd, - polygonStart: function() { - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; - }, - polygonEnd: function() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; - d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; - } - }; - function d3_geo_pathCentroidPoint(x, y) { - d3_geo_centroidX0 += x; - d3_geo_centroidY0 += y; - ++d3_geo_centroidZ0; - } - function d3_geo_pathCentroidLineStart() { - var x0, y0; - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - }; - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX1 += z * (x0 + x) / 2; - d3_geo_centroidY1 += z * (y0 + y) / 2; - d3_geo_centroidZ1 += z; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - } - } - function d3_geo_pathCentroidLineEnd() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; - } - function d3_geo_pathCentroidRingStart() { - var x00, y00, x0, y0; - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); - }; - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX1 += z * (x0 + x) / 2; - d3_geo_centroidY1 += z * (y0 + y) / 2; - d3_geo_centroidZ1 += z; - z = y0 * x - x0 * y; - d3_geo_centroidX2 += z * (x0 + x); - d3_geo_centroidY2 += z * (y0 + y); - d3_geo_centroidZ2 += z * 3; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - } - d3_geo_pathCentroid.lineEnd = function() { - nextPoint(x00, y00); - }; - } - function d3_geo_pathContext(context) { - var pointRadius = 4.5; - var stream = { - point: point, - lineStart: function() { - stream.point = pointLineStart; - }, - lineEnd: lineEnd, - polygonStart: function() { - stream.lineEnd = lineEndPolygon; - }, - polygonEnd: function() { - stream.lineEnd = lineEnd; - stream.point = point; - }, - pointRadius: function(_) { - pointRadius = _; - return stream; - }, - result: d3_noop - }; - function point(x, y) { - context.moveTo(x + pointRadius, y); - context.arc(x, y, pointRadius, 0, τ); - } - function pointLineStart(x, y) { - context.moveTo(x, y); - stream.point = pointLine; - } - function pointLine(x, y) { - context.lineTo(x, y); - } - function lineEnd() { - stream.point = point; - } - function lineEndPolygon() { - context.closePath(); - } - return stream; - } - function d3_geo_resample(project) { - var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; - function resample(stream) { - return (maxDepth ? resampleRecursive : resampleNone)(stream); - } - function resampleNone(stream) { - return d3_geo_transformPoint(stream, function(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - }); - } - function resampleRecursive(stream) { - var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; - var resample = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - stream.polygonStart(); - resample.lineStart = ringStart; - }, - polygonEnd: function() { - stream.polygonEnd(); - resample.lineStart = lineStart; - } - }; - function point(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - } - function lineStart() { - x0 = NaN; - resample.point = linePoint; - stream.lineStart(); - } - function linePoint(λ, φ) { - var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); - resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); - stream.point(x0, y0); - } - function lineEnd() { - resample.point = point; - stream.lineEnd(); - } - function ringStart() { - lineStart(); - resample.point = ringPoint; - resample.lineEnd = ringEnd; - } - function ringPoint(λ, φ) { - linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; - resample.point = linePoint; - } - function ringEnd() { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); - resample.lineEnd = lineEnd; - lineEnd(); - } - return resample; - } - function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { - var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; - if (d2 > 4 * δ2 && depth--) { - var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; - if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); - stream.point(x2, y2); - resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); - } - } - } - resample.precision = function(_) { - if (!arguments.length) return Math.sqrt(δ2); - maxDepth = (δ2 = _ * _) > 0 && 16; - return resample; - }; - return resample; - } - d3.geo.path = function() { - var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; - function path(object) { - if (object) { - if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); - if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); - d3.geo.stream(object, cacheStream); - } - return contextStream.result(); - } - path.area = function(object) { - d3_geo_pathAreaSum = 0; - d3.geo.stream(object, projectStream(d3_geo_pathArea)); - return d3_geo_pathAreaSum; - }; - path.centroid = function(object) { - d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; - d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); - return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; - }; - path.bounds = function(object) { - d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); - d3.geo.stream(object, projectStream(d3_geo_pathBounds)); - return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; - }; - path.projection = function(_) { - if (!arguments.length) return projection; - projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; - return reset(); - }; - path.context = function(_) { - if (!arguments.length) return context; - contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); - if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); - return reset(); - }; - path.pointRadius = function(_) { - if (!arguments.length) return pointRadius; - pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); - return path; - }; - function reset() { - cacheStream = null; - return path; - } - return path.projection(d3.geo.albersUsa()).context(null); - }; - function d3_geo_pathProjectStream(project) { - var resample = d3_geo_resample(function(x, y) { - return project([ x * d3_degrees, y * d3_degrees ]); - }); - return function(stream) { - return d3_geo_projectionRadians(resample(stream)); - }; - } - d3.geo.transform = function(methods) { - return { - stream: function(stream) { - var transform = new d3_geo_transform(stream); - for (var k in methods) transform[k] = methods[k]; - return transform; - } - }; - }; - function d3_geo_transform(stream) { - this.stream = stream; - } - d3_geo_transform.prototype = { - point: function(x, y) { - this.stream.point(x, y); - }, - sphere: function() { - this.stream.sphere(); - }, - lineStart: function() { - this.stream.lineStart(); - }, - lineEnd: function() { - this.stream.lineEnd(); - }, - polygonStart: function() { - this.stream.polygonStart(); - }, - polygonEnd: function() { - this.stream.polygonEnd(); - } - }; - function d3_geo_transformPoint(stream, point) { - return { - point: point, - sphere: function() { - stream.sphere(); - }, - lineStart: function() { - stream.lineStart(); - }, - lineEnd: function() { - stream.lineEnd(); - }, - polygonStart: function() { - stream.polygonStart(); - }, - polygonEnd: function() { - stream.polygonEnd(); - } - }; - } - d3.geo.projection = d3_geo_projection; - d3.geo.projectionMutator = d3_geo_projectionMutator; - function d3_geo_projection(project) { - return d3_geo_projectionMutator(function() { - return project; - })(); - } - function d3_geo_projectionMutator(projectAt) { - var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { - x = project(x, y); - return [ x[0] * k + δx, δy - x[1] * k ]; - }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; - function projection(point) { - point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); - return [ point[0] * k + δx, δy - point[1] * k ]; - } - function invert(point) { - point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); - return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; - } - projection.stream = function(output) { - if (stream) stream.valid = false; - stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); - stream.valid = true; - return stream; - }; - projection.clipAngle = function(_) { - if (!arguments.length) return clipAngle; - preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); - return invalidate(); - }; - projection.clipExtent = function(_) { - if (!arguments.length) return clipExtent; - clipExtent = _; - postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; - return invalidate(); - }; - projection.scale = function(_) { - if (!arguments.length) return k; - k = +_; - return reset(); - }; - projection.translate = function(_) { - if (!arguments.length) return [ x, y ]; - x = +_[0]; - y = +_[1]; - return reset(); - }; - projection.center = function(_) { - if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; - λ = _[0] % 360 * d3_radians; - φ = _[1] % 360 * d3_radians; - return reset(); - }; - projection.rotate = function(_) { - if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; - δλ = _[0] % 360 * d3_radians; - δφ = _[1] % 360 * d3_radians; - δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; - return reset(); - }; - d3.rebind(projection, projectResample, "precision"); - function reset() { - projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); - var center = project(λ, φ); - δx = x - center[0] * k; - δy = y + center[1] * k; - return invalidate(); - } - function invalidate() { - if (stream) stream.valid = false, stream = null; - return projection; - } - return function() { - project = projectAt.apply(this, arguments); - projection.invert = project.invert && invert; - return reset(); - }; - } - function d3_geo_projectionRadians(stream) { - return d3_geo_transformPoint(stream, function(x, y) { - stream.point(x * d3_radians, y * d3_radians); - }); - } - function d3_geo_equirectangular(λ, φ) { - return [ λ, φ ]; - } - (d3.geo.equirectangular = function() { - return d3_geo_projection(d3_geo_equirectangular); - }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; - d3.geo.rotation = function(rotate) { - rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); - function forward(coordinates) { - coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; - } - forward.invert = function(coordinates) { - coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; - }; - return forward; - }; - function d3_geo_identityRotation(λ, φ) { - return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; - } - d3_geo_identityRotation.invert = d3_geo_equirectangular; - function d3_geo_rotation(δλ, δφ, δγ) { - return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; - } - function d3_geo_forwardRotationλ(δλ) { - return function(λ, φ) { - return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; - }; - } - function d3_geo_rotationλ(δλ) { - var rotation = d3_geo_forwardRotationλ(δλ); - rotation.invert = d3_geo_forwardRotationλ(-δλ); - return rotation; - } - function d3_geo_rotationφγ(δφ, δγ) { - var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); - function rotation(λ, φ) { - var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; - return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; - } - rotation.invert = function(λ, φ) { - var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; - return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; - }; - return rotation; - } - d3.geo.circle = function() { - var origin = [ 0, 0 ], angle, precision = 6, interpolate; - function circle() { - var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; - interpolate(null, null, 1, { - point: function(x, y) { - ring.push(x = rotate(x, y)); - x[0] *= d3_degrees, x[1] *= d3_degrees; - } - }); - return { - type: "Polygon", - coordinates: [ ring ] - }; - } - circle.origin = function(x) { - if (!arguments.length) return origin; - origin = x; - return circle; - }; - circle.angle = function(x) { - if (!arguments.length) return angle; - interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); - return circle; - }; - circle.precision = function(_) { - if (!arguments.length) return precision; - interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); - return circle; - }; - return circle.angle(90); - }; - function d3_geo_circleInterpolate(radius, precision) { - var cr = Math.cos(radius), sr = Math.sin(radius); - return function(from, to, direction, listener) { - var step = direction * precision; - if (from != null) { - from = d3_geo_circleAngle(cr, from); - to = d3_geo_circleAngle(cr, to); - if (direction > 0 ? from < to : from > to) from += direction * τ; - } else { - from = radius + direction * τ; - to = radius - .5 * step; - } - for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { - listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); - } - }; - } - function d3_geo_circleAngle(cr, point) { - var a = d3_geo_cartesian(point); - a[0] -= cr; - d3_geo_cartesianNormalize(a); - var angle = d3_acos(-a[1]); - return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); - } - d3.geo.distance = function(a, b) { - var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; - return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); - }; - d3.geo.graticule = function() { - var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5; - function graticule() { - return { - type: "MultiLineString", - coordinates: lines() - }; - } - function lines() { - return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { - return abs(x % DX) > ε; - }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { - return abs(y % DY) > ε; - }).map(y)); - } - graticule.lines = function() { - return lines().map(function(coordinates) { - return { - type: "LineString", - coordinates: coordinates - }; - }); - }; - graticule.outline = function() { - return { - type: "Polygon", - coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ] - }; - }; - graticule.extent = function(_) { - if (!arguments.length) return graticule.minorExtent(); - return graticule.majorExtent(_).minorExtent(_); - }; - graticule.majorExtent = function(_) { - if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ]; - X0 = +_[0][0], X1 = +_[1][0]; - Y0 = +_[0][1], Y1 = +_[1][1]; - if (X0 > X1) _ = X0, X0 = X1, X1 = _; - if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; - return graticule.precision(precision); - }; - graticule.minorExtent = function(_) { - if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; - x0 = +_[0][0], x1 = +_[1][0]; - y0 = +_[0][1], y1 = +_[1][1]; - if (x0 > x1) _ = x0, x0 = x1, x1 = _; - if (y0 > y1) _ = y0, y0 = y1, y1 = _; - return graticule.precision(precision); - }; - graticule.step = function(_) { - if (!arguments.length) return graticule.minorStep(); - return graticule.majorStep(_).minorStep(_); - }; - graticule.majorStep = function(_) { - if (!arguments.length) return [ DX, DY ]; - DX = +_[0], DY = +_[1]; - return graticule; - }; - graticule.minorStep = function(_) { - if (!arguments.length) return [ dx, dy ]; - dx = +_[0], dy = +_[1]; - return graticule; - }; - graticule.precision = function(_) { - if (!arguments.length) return precision; - precision = +_; - x = d3_geo_graticuleX(y0, y1, 90); - y = d3_geo_graticuleY(x0, x1, precision); - X = d3_geo_graticuleX(Y0, Y1, 90); - Y = d3_geo_graticuleY(X0, X1, precision); - return graticule; - }; - return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); - }; - function d3_geo_graticuleX(y0, y1, dy) { - var y = d3.range(y0, y1 - ε, dy).concat(y1); - return function(x) { - return y.map(function(y) { - return [ x, y ]; - }); - }; - } - function d3_geo_graticuleY(x0, x1, dx) { - var x = d3.range(x0, x1 - ε, dx).concat(x1); - return function(y) { - return x.map(function(x) { - return [ x, y ]; - }); - }; - } - function d3_source(d) { - return d.source; - } - function d3_target(d) { - return d.target; - } - d3.geo.greatArc = function() { - var source = d3_source, source_, target = d3_target, target_; - function greatArc() { - return { - type: "LineString", - coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] - }; - } - greatArc.distance = function() { - return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); - }; - greatArc.source = function(_) { - if (!arguments.length) return source; - source = _, source_ = typeof _ === "function" ? null : _; - return greatArc; - }; - greatArc.target = function(_) { - if (!arguments.length) return target; - target = _, target_ = typeof _ === "function" ? null : _; - return greatArc; - }; - greatArc.precision = function() { - return arguments.length ? greatArc : 0; - }; - return greatArc; - }; - d3.geo.interpolate = function(source, target) { - return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); - }; - function d3_geo_interpolate(x0, y0, x1, y1) { - var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); - var interpolate = d ? function(t) { - var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; - return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; - } : function() { - return [ x0 * d3_degrees, y0 * d3_degrees ]; - }; - interpolate.distance = d; - return interpolate; - } - d3.geo.length = function(object) { - d3_geo_lengthSum = 0; - d3.geo.stream(object, d3_geo_length); - return d3_geo_lengthSum; - }; - var d3_geo_lengthSum; - var d3_geo_length = { - sphere: d3_noop, - point: d3_noop, - lineStart: d3_geo_lengthLineStart, - lineEnd: d3_noop, - polygonStart: d3_noop, - polygonEnd: d3_noop - }; - function d3_geo_lengthLineStart() { - var λ0, sinφ0, cosφ0; - d3_geo_length.point = function(λ, φ) { - λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); - d3_geo_length.point = nextPoint; - }; - d3_geo_length.lineEnd = function() { - d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; - }; - function nextPoint(λ, φ) { - var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); - d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); - λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; - } - } - function d3_geo_azimuthal(scale, angle) { - function azimuthal(λ, φ) { - var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); - return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; - } - azimuthal.invert = function(x, y) { - var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); - return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; - }; - return azimuthal; - } - var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { - return Math.sqrt(2 / (1 + cosλcosφ)); - }, function(ρ) { - return 2 * Math.asin(ρ / 2); - }); - (d3.geo.azimuthalEqualArea = function() { - return d3_geo_projection(d3_geo_azimuthalEqualArea); - }).raw = d3_geo_azimuthalEqualArea; - var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { - var c = Math.acos(cosλcosφ); - return c && c / Math.sin(c); - }, d3_identity); - (d3.geo.azimuthalEquidistant = function() { - return d3_geo_projection(d3_geo_azimuthalEquidistant); - }).raw = d3_geo_azimuthalEquidistant; - function d3_geo_conicConformal(φ0, φ1) { - var cosφ0 = Math.cos(φ0), t = function(φ) { - return Math.tan(π / 4 + φ / 2); - }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; - if (!n) return d3_geo_mercator; - function forward(λ, φ) { - if (F > 0) { - if (φ < -halfπ + ε) φ = -halfπ + ε; - } else { - if (φ > halfπ - ε) φ = halfπ - ε; - } - var ρ = F / Math.pow(t(φ), n); - return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y); - return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ]; - }; - return forward; - } - (d3.geo.conicConformal = function() { - return d3_geo_conic(d3_geo_conicConformal); - }).raw = d3_geo_conicConformal; - function d3_geo_conicEquidistant(φ0, φ1) { - var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; - if (abs(n) < ε) return d3_geo_equirectangular; - function forward(λ, φ) { - var ρ = G - φ; - return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = G - y; - return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ]; - }; - return forward; - } - (d3.geo.conicEquidistant = function() { - return d3_geo_conic(d3_geo_conicEquidistant); - }).raw = d3_geo_conicEquidistant; - var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { - return 1 / cosλcosφ; - }, Math.atan); - (d3.geo.gnomonic = function() { - return d3_geo_projection(d3_geo_gnomonic); - }).raw = d3_geo_gnomonic; - function d3_geo_mercator(λ, φ) { - return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ]; - } - d3_geo_mercator.invert = function(x, y) { - return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; - }; - function d3_geo_mercatorProjection(project) { - var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; - m.scale = function() { - var v = scale.apply(m, arguments); - return v === m ? clipAuto ? m.clipExtent(null) : m : v; - }; - m.translate = function() { - var v = translate.apply(m, arguments); - return v === m ? clipAuto ? m.clipExtent(null) : m : v; - }; - m.clipExtent = function(_) { - var v = clipExtent.apply(m, arguments); - if (v === m) { - if (clipAuto = _ == null) { - var k = π * scale(), t = translate(); - clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); - } - } else if (clipAuto) { - v = null; - } - return v; - }; - return m.clipExtent(null); - } - (d3.geo.mercator = function() { - return d3_geo_mercatorProjection(d3_geo_mercator); - }).raw = d3_geo_mercator; - var d3_geo_orthographic = d3_geo_azimuthal(function() { - return 1; - }, Math.asin); - (d3.geo.orthographic = function() { - return d3_geo_projection(d3_geo_orthographic); - }).raw = d3_geo_orthographic; - var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { - return 1 / (1 + cosλcosφ); - }, function(ρ) { - return 2 * Math.atan(ρ); - }); - (d3.geo.stereographic = function() { - return d3_geo_projection(d3_geo_stereographic); - }).raw = d3_geo_stereographic; - function d3_geo_transverseMercator(λ, φ) { - return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ]; - } - d3_geo_transverseMercator.invert = function(x, y) { - return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ]; - }; - (d3.geo.transverseMercator = function() { - var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; - projection.center = function(_) { - return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]); - }; - projection.rotate = function(_) { - return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), - [ _[0], _[1], _[2] - 90 ]); - }; - return rotate([ 0, 0, 90 ]); - }).raw = d3_geo_transverseMercator; - d3.geom = {}; - function d3_geom_pointX(d) { - return d[0]; - } - function d3_geom_pointY(d) { - return d[1]; - } - d3.geom.hull = function(vertices) { - var x = d3_geom_pointX, y = d3_geom_pointY; - if (arguments.length) return hull(vertices); - function hull(data) { - if (data.length < 3) return []; - var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; - for (i = 0; i < n; i++) { - points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); - } - points.sort(d3_geom_hullOrder); - for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); - var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); - var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; - for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); - for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); - return polygon; - } - hull.x = function(_) { - return arguments.length ? (x = _, hull) : x; - }; - hull.y = function(_) { - return arguments.length ? (y = _, hull) : y; - }; - return hull; - }; - function d3_geom_hullUpper(points) { - var n = points.length, hull = [ 0, 1 ], hs = 2; - for (var i = 2; i < n; i++) { - while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; - hull[hs++] = i; - } - return hull.slice(0, hs); - } - function d3_geom_hullOrder(a, b) { - return a[0] - b[0] || a[1] - b[1]; - } - d3.geom.polygon = function(coordinates) { - d3_subclass(coordinates, d3_geom_polygonPrototype); - return coordinates; - }; - var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; - d3_geom_polygonPrototype.area = function() { - var i = -1, n = this.length, a, b = this[n - 1], area = 0; - while (++i < n) { - a = b; - b = this[i]; - area += a[1] * b[0] - a[0] * b[1]; - } - return area * .5; - }; - d3_geom_polygonPrototype.centroid = function(k) { - var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; - if (!arguments.length) k = -1 / (6 * this.area()); - while (++i < n) { - a = b; - b = this[i]; - c = a[0] * b[1] - b[0] * a[1]; - x += (a[0] + b[0]) * c; - y += (a[1] + b[1]) * c; - } - return [ x * k, y * k ]; - }; - d3_geom_polygonPrototype.clip = function(subject) { - var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; - while (++i < n) { - input = subject.slice(); - subject.length = 0; - b = this[i]; - c = input[(m = input.length - closed) - 1]; - j = -1; - while (++j < m) { - d = input[j]; - if (d3_geom_polygonInside(d, a, b)) { - if (!d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - subject.push(d); - } else if (d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - c = d; - } - if (closed) subject.push(subject[0]); - a = b; - } - return subject; - }; - function d3_geom_polygonInside(p, a, b) { - return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); - } - function d3_geom_polygonIntersect(c, d, a, b) { - var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); - return [ x1 + ua * x21, y1 + ua * y21 ]; - } - function d3_geom_polygonClosed(coordinates) { - var a = coordinates[0], b = coordinates[coordinates.length - 1]; - return !(a[0] - b[0] || a[1] - b[1]); - } - var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; - function d3_geom_voronoiBeach() { - d3_geom_voronoiRedBlackNode(this); - this.edge = this.site = this.circle = null; - } - function d3_geom_voronoiCreateBeach(site) { - var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); - beach.site = site; - return beach; - } - function d3_geom_voronoiDetachBeach(beach) { - d3_geom_voronoiDetachCircle(beach); - d3_geom_voronoiBeaches.remove(beach); - d3_geom_voronoiBeachPool.push(beach); - d3_geom_voronoiRedBlackNode(beach); - } - function d3_geom_voronoiRemoveBeach(beach) { - var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { - x: x, - y: y - }, previous = beach.P, next = beach.N, disappearing = [ beach ]; - d3_geom_voronoiDetachBeach(beach); - var lArc = previous; - while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { - previous = lArc.P; - disappearing.unshift(lArc); - d3_geom_voronoiDetachBeach(lArc); - lArc = previous; - } - disappearing.unshift(lArc); - d3_geom_voronoiDetachCircle(lArc); - var rArc = next; - while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { - next = rArc.N; - disappearing.push(rArc); - d3_geom_voronoiDetachBeach(rArc); - rArc = next; - } - disappearing.push(rArc); - d3_geom_voronoiDetachCircle(rArc); - var nArcs = disappearing.length, iArc; - for (iArc = 1; iArc < nArcs; ++iArc) { - rArc = disappearing[iArc]; - lArc = disappearing[iArc - 1]; - d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); - } - lArc = disappearing[0]; - rArc = disappearing[nArcs - 1]; - rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - } - function d3_geom_voronoiAddBeach(site) { - var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; - while (node) { - dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; - if (dxl > ε) node = node.L; else { - dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); - if (dxr > ε) { - if (!node.R) { - lArc = node; - break; - } - node = node.R; - } else { - if (dxl > -ε) { - lArc = node.P; - rArc = node; - } else if (dxr > -ε) { - lArc = node; - rArc = node.N; - } else { - lArc = rArc = node; - } - break; - } - } - } - var newArc = d3_geom_voronoiCreateBeach(site); - d3_geom_voronoiBeaches.insert(lArc, newArc); - if (!lArc && !rArc) return; - if (lArc === rArc) { - d3_geom_voronoiDetachCircle(lArc); - rArc = d3_geom_voronoiCreateBeach(lArc.site); - d3_geom_voronoiBeaches.insert(newArc, rArc); - newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - return; - } - if (!rArc) { - newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); - return; - } - d3_geom_voronoiDetachCircle(lArc); - d3_geom_voronoiDetachCircle(rArc); - var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { - x: (cy * hb - by * hc) / d + ax, - y: (bx * hc - cx * hb) / d + ay - }; - d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); - newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); - rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - } - function d3_geom_voronoiLeftBreakPoint(arc, directrix) { - var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; - if (!pby2) return rfocx; - var lArc = arc.P; - if (!lArc) return -Infinity; - site = lArc.site; - var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix; - if (!plby2) return lfocx; - var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2; - if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; - return (rfocx + lfocx) / 2; - } - function d3_geom_voronoiRightBreakPoint(arc, directrix) { - var rArc = arc.N; - if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); - var site = arc.site; - return site.y === directrix ? site.x : Infinity; - } - function d3_geom_voronoiCell(site) { - this.site = site; - this.edges = []; - } - d3_geom_voronoiCell.prototype.prepare = function() { - var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; - while (iHalfEdge--) { - edge = halfEdges[iHalfEdge].edge; - if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); - } - halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); - return halfEdges.length; - }; - function d3_geom_voronoiCloseCells(extent) { - var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; - while (iCell--) { - cell = cells[iCell]; - if (!cell || !cell.prepare()) continue; - halfEdges = cell.edges; - nHalfEdges = halfEdges.length; - iHalfEdge = 0; - while (iHalfEdge < nHalfEdges) { - end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; - start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; - if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { - halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { - x: x0, - y: abs(x2 - x0) < ε ? y2 : y1 - } : abs(y3 - y1) < ε && x1 - x3 > ε ? { - x: abs(y2 - y1) < ε ? x2 : x1, - y: y1 - } : abs(x3 - x1) < ε && y3 - y0 > ε ? { - x: x1, - y: abs(x2 - x1) < ε ? y2 : y0 - } : abs(y3 - y0) < ε && x3 - x0 > ε ? { - x: abs(y2 - y0) < ε ? x2 : x0, - y: y0 - } : null), cell.site, null)); - ++nHalfEdges; - } - } - } - } - function d3_geom_voronoiHalfEdgeOrder(a, b) { - return b.angle - a.angle; - } - function d3_geom_voronoiCircle() { - d3_geom_voronoiRedBlackNode(this); - this.x = this.y = this.arc = this.site = this.cy = null; - } - function d3_geom_voronoiAttachCircle(arc) { - var lArc = arc.P, rArc = arc.N; - if (!lArc || !rArc) return; - var lSite = lArc.site, cSite = arc.site, rSite = rArc.site; - if (lSite === rSite) return; - var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; - var d = 2 * (ax * cy - ay * cx); - if (d >= -ε2) return; - var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by; - var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); - circle.arc = arc; - circle.site = cSite; - circle.x = x + bx; - circle.y = cy + Math.sqrt(x * x + y * y); - circle.cy = cy; - arc.circle = circle; - var before = null, node = d3_geom_voronoiCircles._; - while (node) { - if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) { - if (node.L) node = node.L; else { - before = node.P; - break; - } - } else { - if (node.R) node = node.R; else { - before = node; - break; - } - } - } - d3_geom_voronoiCircles.insert(before, circle); - if (!before) d3_geom_voronoiFirstCircle = circle; - } - function d3_geom_voronoiDetachCircle(arc) { - var circle = arc.circle; - if (circle) { - if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; - d3_geom_voronoiCircles.remove(circle); - d3_geom_voronoiCirclePool.push(circle); - d3_geom_voronoiRedBlackNode(circle); - arc.circle = null; - } - } - function d3_geom_voronoiClipEdges(extent) { - var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; - while (i--) { - e = edges[i]; - if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { - e.a = e.b = null; - edges.splice(i, 1); - } - } - } - function d3_geom_voronoiConnectEdge(edge, extent) { - var vb = edge.b; - if (vb) return true; - var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; - if (ry === ly) { - if (fx < x0 || fx >= x1) return; - if (lx > rx) { - if (!va) va = { - x: fx, - y: y0 - }; else if (va.y >= y1) return; - vb = { - x: fx, - y: y1 - }; - } else { - if (!va) va = { - x: fx, - y: y1 - }; else if (va.y < y0) return; - vb = { - x: fx, - y: y0 - }; - } - } else { - fm = (lx - rx) / (ry - ly); - fb = fy - fm * fx; - if (fm < -1 || fm > 1) { - if (lx > rx) { - if (!va) va = { - x: (y0 - fb) / fm, - y: y0 - }; else if (va.y >= y1) return; - vb = { - x: (y1 - fb) / fm, - y: y1 - }; - } else { - if (!va) va = { - x: (y1 - fb) / fm, - y: y1 - }; else if (va.y < y0) return; - vb = { - x: (y0 - fb) / fm, - y: y0 - }; - } - } else { - if (ly < ry) { - if (!va) va = { - x: x0, - y: fm * x0 + fb - }; else if (va.x >= x1) return; - vb = { - x: x1, - y: fm * x1 + fb - }; - } else { - if (!va) va = { - x: x1, - y: fm * x1 + fb - }; else if (va.x < x0) return; - vb = { - x: x0, - y: fm * x0 + fb - }; - } - } - } - edge.a = va; - edge.b = vb; - return true; - } - function d3_geom_voronoiEdge(lSite, rSite) { - this.l = lSite; - this.r = rSite; - this.a = this.b = null; - } - function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { - var edge = new d3_geom_voronoiEdge(lSite, rSite); - d3_geom_voronoiEdges.push(edge); - if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); - if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); - d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); - d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); - return edge; - } - function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { - var edge = new d3_geom_voronoiEdge(lSite, null); - edge.a = va; - edge.b = vb; - d3_geom_voronoiEdges.push(edge); - return edge; - } - function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { - if (!edge.a && !edge.b) { - edge.a = vertex; - edge.l = lSite; - edge.r = rSite; - } else if (edge.l === rSite) { - edge.b = vertex; - } else { - edge.a = vertex; - } - } - function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { - var va = edge.a, vb = edge.b; - this.edge = edge; - this.site = lSite; - this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); - } - d3_geom_voronoiHalfEdge.prototype = { - start: function() { - return this.edge.l === this.site ? this.edge.a : this.edge.b; - }, - end: function() { - return this.edge.l === this.site ? this.edge.b : this.edge.a; - } - }; - function d3_geom_voronoiRedBlackTree() { - this._ = null; - } - function d3_geom_voronoiRedBlackNode(node) { - node.U = node.C = node.L = node.R = node.P = node.N = null; - } - d3_geom_voronoiRedBlackTree.prototype = { - insert: function(after, node) { - var parent, grandpa, uncle; - if (after) { - node.P = after; - node.N = after.N; - if (after.N) after.N.P = node; - after.N = node; - if (after.R) { - after = after.R; - while (after.L) after = after.L; - after.L = node; - } else { - after.R = node; - } - parent = after; - } else if (this._) { - after = d3_geom_voronoiRedBlackFirst(this._); - node.P = null; - node.N = after; - after.P = after.L = node; - parent = after; - } else { - node.P = node.N = null; - this._ = node; - parent = null; - } - node.L = node.R = null; - node.U = parent; - node.C = true; - after = node; - while (parent && parent.C) { - grandpa = parent.U; - if (parent === grandpa.L) { - uncle = grandpa.R; - if (uncle && uncle.C) { - parent.C = uncle.C = false; - grandpa.C = true; - after = grandpa; - } else { - if (after === parent.R) { - d3_geom_voronoiRedBlackRotateLeft(this, parent); - after = parent; - parent = after.U; - } - parent.C = false; - grandpa.C = true; - d3_geom_voronoiRedBlackRotateRight(this, grandpa); - } - } else { - uncle = grandpa.L; - if (uncle && uncle.C) { - parent.C = uncle.C = false; - grandpa.C = true; - after = grandpa; - } else { - if (after === parent.L) { - d3_geom_voronoiRedBlackRotateRight(this, parent); - after = parent; - parent = after.U; - } - parent.C = false; - grandpa.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, grandpa); - } - } - parent = after.U; - } - this._.C = false; - }, - remove: function(node) { - if (node.N) node.N.P = node.P; - if (node.P) node.P.N = node.N; - node.N = node.P = null; - var parent = node.U, sibling, left = node.L, right = node.R, next, red; - if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right); - if (parent) { - if (parent.L === node) parent.L = next; else parent.R = next; - } else { - this._ = next; - } - if (left && right) { - red = next.C; - next.C = node.C; - next.L = left; - left.U = next; - if (next !== right) { - parent = next.U; - next.U = node.U; - node = next.R; - parent.L = node; - next.R = right; - right.U = next; - } else { - next.U = parent; - parent = next; - node = next.R; - } - } else { - red = node.C; - node = next; - } - if (node) node.U = parent; - if (red) return; - if (node && node.C) { - node.C = false; - return; - } - do { - if (node === this._) break; - if (node === parent.L) { - sibling = parent.R; - if (sibling.C) { - sibling.C = false; - parent.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, parent); - sibling = parent.R; - } - if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { - if (!sibling.R || !sibling.R.C) { - sibling.L.C = false; - sibling.C = true; - d3_geom_voronoiRedBlackRotateRight(this, sibling); - sibling = parent.R; - } - sibling.C = parent.C; - parent.C = sibling.R.C = false; - d3_geom_voronoiRedBlackRotateLeft(this, parent); - node = this._; - break; - } - } else { - sibling = parent.L; - if (sibling.C) { - sibling.C = false; - parent.C = true; - d3_geom_voronoiRedBlackRotateRight(this, parent); - sibling = parent.L; - } - if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { - if (!sibling.L || !sibling.L.C) { - sibling.R.C = false; - sibling.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, sibling); - sibling = parent.L; - } - sibling.C = parent.C; - parent.C = sibling.L.C = false; - d3_geom_voronoiRedBlackRotateRight(this, parent); - node = this._; - break; - } - } - sibling.C = true; - node = parent; - parent = parent.U; - } while (!node.C); - if (node) node.C = false; - } - }; - function d3_geom_voronoiRedBlackRotateLeft(tree, node) { - var p = node, q = node.R, parent = p.U; - if (parent) { - if (parent.L === p) parent.L = q; else parent.R = q; - } else { - tree._ = q; - } - q.U = parent; - p.U = q; - p.R = q.L; - if (p.R) p.R.U = p; - q.L = p; - } - function d3_geom_voronoiRedBlackRotateRight(tree, node) { - var p = node, q = node.L, parent = p.U; - if (parent) { - if (parent.L === p) parent.L = q; else parent.R = q; - } else { - tree._ = q; - } - q.U = parent; - p.U = q; - p.L = q.R; - if (p.L) p.L.U = p; - q.R = p; - } - function d3_geom_voronoiRedBlackFirst(node) { - while (node.L) node = node.L; - return node; - } - function d3_geom_voronoi(sites, bbox) { - var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; - d3_geom_voronoiEdges = []; - d3_geom_voronoiCells = new Array(sites.length); - d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); - d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); - while (true) { - circle = d3_geom_voronoiFirstCircle; - if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { - if (site.x !== x0 || site.y !== y0) { - d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); - d3_geom_voronoiAddBeach(site); - x0 = site.x, y0 = site.y; - } - site = sites.pop(); - } else if (circle) { - d3_geom_voronoiRemoveBeach(circle.arc); - } else { - break; - } - } - if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); - var diagram = { - cells: d3_geom_voronoiCells, - edges: d3_geom_voronoiEdges - }; - d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; - return diagram; - } - function d3_geom_voronoiVertexOrder(a, b) { - return b.y - a.y || b.x - a.x; - } - d3.geom.voronoi = function(points) { - var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; - if (points) return voronoi(points); - function voronoi(data) { - var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; - d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { - var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { - var s = e.start(); - return [ s.x, s.y ]; - }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; - polygon.point = data[i]; - }); - return polygons; - } - function sites(data) { - return data.map(function(d, i) { - return { - x: Math.round(fx(d, i) / ε) * ε, - y: Math.round(fy(d, i) / ε) * ε, - i: i - }; - }); - } - voronoi.links = function(data) { - return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { - return edge.l && edge.r; - }).map(function(edge) { - return { - source: data[edge.l.i], - target: data[edge.r.i] - }; - }); - }; - voronoi.triangles = function(data) { - var triangles = []; - d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { - var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; - while (++j < m) { - e0 = e1; - s0 = s1; - e1 = edges[j].edge; - s1 = e1.l === site ? e1.r : e1.l; - if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { - triangles.push([ data[i], data[s0.i], data[s1.i] ]); - } - } - }); - return triangles; - }; - voronoi.x = function(_) { - return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; - }; - voronoi.y = function(_) { - return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; - }; - voronoi.clipExtent = function(_) { - if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; - clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; - return voronoi; - }; - voronoi.size = function(_) { - if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; - return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); - }; - return voronoi; - }; - var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; - function d3_geom_voronoiTriangleArea(a, b, c) { - return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); - } - d3.geom.delaunay = function(vertices) { - return d3.geom.voronoi().triangles(vertices); - }; - d3.geom.quadtree = function(points, x1, y1, x2, y2) { - var x = d3_geom_pointX, y = d3_geom_pointY, compat; - if (compat = arguments.length) { - x = d3_geom_quadtreeCompatX; - y = d3_geom_quadtreeCompatY; - if (compat === 3) { - y2 = y1; - x2 = x1; - y1 = x1 = 0; - } - return quadtree(points); - } - function quadtree(data) { - var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; - if (x1 != null) { - x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; - } else { - x2_ = y2_ = -(x1_ = y1_ = Infinity); - xs = [], ys = []; - n = data.length; - if (compat) for (i = 0; i < n; ++i) { - d = data[i]; - if (d.x < x1_) x1_ = d.x; - if (d.y < y1_) y1_ = d.y; - if (d.x > x2_) x2_ = d.x; - if (d.y > y2_) y2_ = d.y; - xs.push(d.x); - ys.push(d.y); - } else for (i = 0; i < n; ++i) { - var x_ = +fx(d = data[i], i), y_ = +fy(d, i); - if (x_ < x1_) x1_ = x_; - if (y_ < y1_) y1_ = y_; - if (x_ > x2_) x2_ = x_; - if (y_ > y2_) y2_ = y_; - xs.push(x_); - ys.push(y_); - } - } - var dx = x2_ - x1_, dy = y2_ - y1_; - if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; - function insert(n, d, x, y, x1, y1, x2, y2) { - if (isNaN(x) || isNaN(y)) return; - if (n.leaf) { - var nx = n.x, ny = n.y; - if (nx != null) { - if (abs(nx - x) + abs(ny - y) < .01) { - insertChild(n, d, x, y, x1, y1, x2, y2); - } else { - var nPoint = n.point; - n.x = n.y = n.point = null; - insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); - insertChild(n, d, x, y, x1, y1, x2, y2); - } - } else { - n.x = x, n.y = y, n.point = d; - } - } else { - insertChild(n, d, x, y, x1, y1, x2, y2); - } - } - function insertChild(n, d, x, y, x1, y1, x2, y2) { - var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right; - n.leaf = false; - n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); - if (right) x1 = xm; else x2 = xm; - if (below) y1 = ym; else y2 = ym; - insert(n, d, x, y, x1, y1, x2, y2); - } - var root = d3_geom_quadtreeNode(); - root.add = function(d) { - insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); - }; - root.visit = function(f) { - d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); - }; - root.find = function(point) { - return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_); - }; - i = -1; - if (x1 == null) { - while (++i < n) { - insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); - } - --i; - } else data.forEach(root.add); - xs = ys = data = d = null; - return root; - } - quadtree.x = function(_) { - return arguments.length ? (x = _, quadtree) : x; - }; - quadtree.y = function(_) { - return arguments.length ? (y = _, quadtree) : y; - }; - quadtree.extent = function(_) { - if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; - if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], - y2 = +_[1][1]; - return quadtree; - }; - quadtree.size = function(_) { - if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; - if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; - return quadtree; - }; - return quadtree; - }; - function d3_geom_quadtreeCompatX(d) { - return d.x; - } - function d3_geom_quadtreeCompatY(d) { - return d.y; - } - function d3_geom_quadtreeNode() { - return { - leaf: true, - nodes: [], - point: null, - x: null, - y: null - }; - } - function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { - if (!f(node, x1, y1, x2, y2)) { - var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; - if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); - if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); - if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); - if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); - } - } - function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) { - var minDistance2 = Infinity, closestPoint; - (function find(node, x1, y1, x2, y2) { - if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return; - if (point = node.point) { - var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy; - if (distance2 < minDistance2) { - var distance = Math.sqrt(minDistance2 = distance2); - x0 = x - distance, y0 = y - distance; - x3 = x + distance, y3 = y + distance; - closestPoint = point; - } - } - var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym; - for (var i = below << 1 | right, j = i + 4; i < j; ++i) { - if (node = children[i & 3]) switch (i & 3) { - case 0: - find(node, x1, y1, xm, ym); - break; - - case 1: - find(node, xm, y1, x2, ym); - break; - - case 2: - find(node, x1, ym, xm, y2); - break; - - case 3: - find(node, xm, ym, x2, y2); - break; - } - } - })(root, x0, y0, x3, y3); - return closestPoint; - } - d3.interpolateRgb = d3_interpolateRgb; - function d3_interpolateRgb(a, b) { - a = d3.rgb(a); - b = d3.rgb(b); - var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; - return function(t) { - return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); - }; - } - d3.interpolateObject = d3_interpolateObject; - function d3_interpolateObject(a, b) { - var i = {}, c = {}, k; - for (k in a) { - if (k in b) { - i[k] = d3_interpolate(a[k], b[k]); - } else { - c[k] = a[k]; - } - } - for (k in b) { - if (!(k in a)) { - c[k] = b[k]; - } - } - return function(t) { - for (k in i) c[k] = i[k](t); - return c; - }; - } - d3.interpolateNumber = d3_interpolateNumber; - function d3_interpolateNumber(a, b) { - a = +a, b = +b; - return function(t) { - return a * (1 - t) + b * t; - }; - } - d3.interpolateString = d3_interpolateString; - function d3_interpolateString(a, b) { - var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = []; - a = a + "", b = b + ""; - while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) { - if ((bs = bm.index) > bi) { - bs = b.slice(bi, bs); - if (s[i]) s[i] += bs; else s[++i] = bs; - } - if ((am = am[0]) === (bm = bm[0])) { - if (s[i]) s[i] += bm; else s[++i] = bm; - } else { - s[++i] = null; - q.push({ - i: i, - x: d3_interpolateNumber(am, bm) - }); - } - bi = d3_interpolate_numberB.lastIndex; - } - if (bi < b.length) { - bs = b.slice(bi); - if (s[i]) s[i] += bs; else s[++i] = bs; - } - return s.length < 2 ? q[0] ? (b = q[0].x, function(t) { - return b(t) + ""; - }) : function() { - return b; - } : (b = q.length, function(t) { - for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }); - } - var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); - d3.interpolate = d3_interpolate; - function d3_interpolate(a, b) { - var i = d3.interpolators.length, f; - while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; - return f; - } - d3.interpolators = [ function(a, b) { - var t = typeof b; - return (t === "string" ? d3_rgb_names.has(b.toLowerCase()) || /^(#|rgb\(|hsl\()/i.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); - } ]; - d3.interpolateArray = d3_interpolateArray; - function d3_interpolateArray(a, b) { - var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; - for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); - for (;i < na; ++i) c[i] = a[i]; - for (;i < nb; ++i) c[i] = b[i]; - return function(t) { - for (i = 0; i < n0; ++i) c[i] = x[i](t); - return c; - }; - } - var d3_ease_default = function() { - return d3_identity; - }; - var d3_ease = d3.map({ - linear: d3_ease_default, - poly: d3_ease_poly, - quad: function() { - return d3_ease_quad; - }, - cubic: function() { - return d3_ease_cubic; - }, - sin: function() { - return d3_ease_sin; - }, - exp: function() { - return d3_ease_exp; - }, - circle: function() { - return d3_ease_circle; - }, - elastic: d3_ease_elastic, - back: d3_ease_back, - bounce: function() { - return d3_ease_bounce; - } - }); - var d3_ease_mode = d3.map({ - "in": d3_identity, - out: d3_ease_reverse, - "in-out": d3_ease_reflect, - "out-in": function(f) { - return d3_ease_reflect(d3_ease_reverse(f)); - } - }); - d3.ease = function(name) { - var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in"; - t = d3_ease.get(t) || d3_ease_default; - m = d3_ease_mode.get(m) || d3_identity; - return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); - }; - function d3_ease_clamp(f) { - return function(t) { - return t <= 0 ? 0 : t >= 1 ? 1 : f(t); - }; - } - function d3_ease_reverse(f) { - return function(t) { - return 1 - f(1 - t); - }; - } - function d3_ease_reflect(f) { - return function(t) { - return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); - }; - } - function d3_ease_quad(t) { - return t * t; - } - function d3_ease_cubic(t) { - return t * t * t; - } - function d3_ease_cubicInOut(t) { - if (t <= 0) return 0; - if (t >= 1) return 1; - var t2 = t * t, t3 = t2 * t; - return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); - } - function d3_ease_poly(e) { - return function(t) { - return Math.pow(t, e); - }; - } - function d3_ease_sin(t) { - return 1 - Math.cos(t * halfπ); - } - function d3_ease_exp(t) { - return Math.pow(2, 10 * (t - 1)); - } - function d3_ease_circle(t) { - return 1 - Math.sqrt(1 - t * t); - } - function d3_ease_elastic(a, p) { - var s; - if (arguments.length < 2) p = .45; - if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4; - return function(t) { - return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); - }; - } - function d3_ease_back(s) { - if (!s) s = 1.70158; - return function(t) { - return t * t * ((s + 1) * t - s); - }; - } - function d3_ease_bounce(t) { - return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; - } - d3.interpolateHcl = d3_interpolateHcl; - function d3_interpolateHcl(a, b) { - a = d3.hcl(a); - b = d3.hcl(b); - var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; - if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; - if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; - return function(t) { - return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; - }; - } - d3.interpolateHsl = d3_interpolateHsl; - function d3_interpolateHsl(a, b) { - a = d3.hsl(a); - b = d3.hsl(b); - var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; - if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; - if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; - return function(t) { - return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; - }; - } - d3.interpolateLab = d3_interpolateLab; - function d3_interpolateLab(a, b) { - a = d3.lab(a); - b = d3.lab(b); - var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; - return function(t) { - return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; - }; - } - d3.interpolateRound = d3_interpolateRound; - function d3_interpolateRound(a, b) { - b -= a; - return function(t) { - return Math.round(a + b * t); - }; - } - d3.transform = function(string) { - var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); - return (d3.transform = function(string) { - if (string != null) { - g.setAttribute("transform", string); - var t = g.transform.baseVal.consolidate(); - } - return new d3_transform(t ? t.matrix : d3_transformIdentity); - })(string); - }; - function d3_transform(m) { - var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; - if (r0[0] * r1[1] < r1[0] * r0[1]) { - r0[0] *= -1; - r0[1] *= -1; - kx *= -1; - kz *= -1; - } - this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; - this.translate = [ m.e, m.f ]; - this.scale = [ kx, ky ]; - this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; - } - d3_transform.prototype.toString = function() { - return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; - }; - function d3_transformDot(a, b) { - return a[0] * b[0] + a[1] * b[1]; - } - function d3_transformNormalize(a) { - var k = Math.sqrt(d3_transformDot(a, a)); - if (k) { - a[0] /= k; - a[1] /= k; - } - return k; - } - function d3_transformCombine(a, b, k) { - a[0] += k * b[0]; - a[1] += k * b[1]; - return a; - } - var d3_transformIdentity = { - a: 1, - b: 0, - c: 0, - d: 1, - e: 0, - f: 0 - }; - d3.interpolateTransform = d3_interpolateTransform; - function d3_interpolateTransformPop(s) { - return s.length ? s.pop() + "," : ""; - } - function d3_interpolateTranslate(ta, tb, s, q) { - if (ta[0] !== tb[0] || ta[1] !== tb[1]) { - var i = s.push("translate(", null, ",", null, ")"); - q.push({ - i: i - 4, - x: d3_interpolateNumber(ta[0], tb[0]) - }, { - i: i - 2, - x: d3_interpolateNumber(ta[1], tb[1]) - }); - } else if (tb[0] || tb[1]) { - s.push("translate(" + tb + ")"); - } - } - function d3_interpolateRotate(ra, rb, s, q) { - if (ra !== rb) { - if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; - q.push({ - i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2, - x: d3_interpolateNumber(ra, rb) - }); - } else if (rb) { - s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")"); - } - } - function d3_interpolateSkew(wa, wb, s, q) { - if (wa !== wb) { - q.push({ - i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2, - x: d3_interpolateNumber(wa, wb) - }); - } else if (wb) { - s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")"); - } - } - function d3_interpolateScale(ka, kb, s, q) { - if (ka[0] !== kb[0] || ka[1] !== kb[1]) { - var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")"); - q.push({ - i: i - 4, - x: d3_interpolateNumber(ka[0], kb[0]) - }, { - i: i - 2, - x: d3_interpolateNumber(ka[1], kb[1]) - }); - } else if (kb[0] !== 1 || kb[1] !== 1) { - s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")"); - } - } - function d3_interpolateTransform(a, b) { - var s = [], q = []; - a = d3.transform(a), b = d3.transform(b); - d3_interpolateTranslate(a.translate, b.translate, s, q); - d3_interpolateRotate(a.rotate, b.rotate, s, q); - d3_interpolateSkew(a.skew, b.skew, s, q); - d3_interpolateScale(a.scale, b.scale, s, q); - a = b = null; - return function(t) { - var i = -1, n = q.length, o; - while (++i < n) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; - } - function d3_uninterpolateNumber(a, b) { - b = (b -= a = +a) || 1 / b; - return function(x) { - return (x - a) / b; - }; - } - function d3_uninterpolateClamp(a, b) { - b = (b -= a = +a) || 1 / b; - return function(x) { - return Math.max(0, Math.min(1, (x - a) / b)); - }; - } - d3.layout = {}; - d3.layout.bundle = function() { - return function(links) { - var paths = [], i = -1, n = links.length; - while (++i < n) paths.push(d3_layout_bundlePath(links[i])); - return paths; - }; - }; - function d3_layout_bundlePath(link) { - var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; - while (start !== lca) { - start = start.parent; - points.push(start); - } - var k = points.length; - while (end !== lca) { - points.splice(k, 0, end); - end = end.parent; - } - return points; - } - function d3_layout_bundleAncestors(node) { - var ancestors = [], parent = node.parent; - while (parent != null) { - ancestors.push(node); - node = parent; - parent = parent.parent; - } - ancestors.push(node); - return ancestors; - } - function d3_layout_bundleLeastCommonAncestor(a, b) { - if (a === b) return a; - var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; - while (aNode === bNode) { - sharedNode = aNode; - aNode = aNodes.pop(); - bNode = bNodes.pop(); - } - return sharedNode; - } - d3.layout.chord = function() { - var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; - function relayout() { - var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; - chords = []; - groups = []; - k = 0, i = -1; - while (++i < n) { - x = 0, j = -1; - while (++j < n) { - x += matrix[i][j]; - } - groupSums.push(x); - subgroupIndex.push(d3.range(n)); - k += x; - } - if (sortGroups) { - groupIndex.sort(function(a, b) { - return sortGroups(groupSums[a], groupSums[b]); - }); - } - if (sortSubgroups) { - subgroupIndex.forEach(function(d, i) { - d.sort(function(a, b) { - return sortSubgroups(matrix[i][a], matrix[i][b]); - }); - }); - } - k = (τ - padding * n) / k; - x = 0, i = -1; - while (++i < n) { - x0 = x, j = -1; - while (++j < n) { - var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; - subgroups[di + "-" + dj] = { - index: di, - subindex: dj, - startAngle: a0, - endAngle: a1, - value: v - }; - } - groups[di] = { - index: di, - startAngle: x0, - endAngle: x, - value: groupSums[di] - }; - x += padding; - } - i = -1; - while (++i < n) { - j = i - 1; - while (++j < n) { - var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; - if (source.value || target.value) { - chords.push(source.value < target.value ? { - source: target, - target: source - } : { - source: source, - target: target - }); - } - } - } - if (sortChords) resort(); - } - function resort() { - chords.sort(function(a, b) { - return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); - }); - } - chord.matrix = function(x) { - if (!arguments.length) return matrix; - n = (matrix = x) && matrix.length; - chords = groups = null; - return chord; - }; - chord.padding = function(x) { - if (!arguments.length) return padding; - padding = x; - chords = groups = null; - return chord; - }; - chord.sortGroups = function(x) { - if (!arguments.length) return sortGroups; - sortGroups = x; - chords = groups = null; - return chord; - }; - chord.sortSubgroups = function(x) { - if (!arguments.length) return sortSubgroups; - sortSubgroups = x; - chords = null; - return chord; - }; - chord.sortChords = function(x) { - if (!arguments.length) return sortChords; - sortChords = x; - if (chords) resort(); - return chord; - }; - chord.chords = function() { - if (!chords) relayout(); - return chords; - }; - chord.groups = function() { - if (!groups) relayout(); - return groups; - }; - return chord; - }; - d3.layout.force = function() { - var force = {}, event = d3.dispatch("start", "tick", "end"), timer, size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; - function repulse(node) { - return function(quad, x1, _, x2) { - if (quad.point !== node) { - var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; - if (dw * dw / theta2 < dn) { - if (dn < chargeDistance2) { - var k = quad.charge / dn; - node.px -= dx * k; - node.py -= dy * k; - } - return true; - } - if (quad.point && dn && dn < chargeDistance2) { - var k = quad.pointCharge / dn; - node.px -= dx * k; - node.py -= dy * k; - } - } - return !quad.charge; - }; - } - force.tick = function() { - if ((alpha *= .99) < .005) { - timer = null; - event.end({ - type: "end", - alpha: alpha = 0 - }); - return true; - } - var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; - for (i = 0; i < m; ++i) { - o = links[i]; - s = o.source; - t = o.target; - x = t.x - s.x; - y = t.y - s.y; - if (l = x * x + y * y) { - l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; - x *= l; - y *= l; - t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5); - t.y -= y * k; - s.x += x * (k = 1 - k); - s.y += y * k; - } - } - if (k = alpha * gravity) { - x = size[0] / 2; - y = size[1] / 2; - i = -1; - if (k) while (++i < n) { - o = nodes[i]; - o.x += (x - o.x) * k; - o.y += (y - o.y) * k; - } - } - if (charge) { - d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); - i = -1; - while (++i < n) { - if (!(o = nodes[i]).fixed) { - q.visit(repulse(o)); - } - } - } - i = -1; - while (++i < n) { - o = nodes[i]; - if (o.fixed) { - o.x = o.px; - o.y = o.py; - } else { - o.x -= (o.px - (o.px = o.x)) * friction; - o.y -= (o.py - (o.py = o.y)) * friction; - } - } - event.tick({ - type: "tick", - alpha: alpha - }); - }; - force.nodes = function(x) { - if (!arguments.length) return nodes; - nodes = x; - return force; - }; - force.links = function(x) { - if (!arguments.length) return links; - links = x; - return force; - }; - force.size = function(x) { - if (!arguments.length) return size; - size = x; - return force; - }; - force.linkDistance = function(x) { - if (!arguments.length) return linkDistance; - linkDistance = typeof x === "function" ? x : +x; - return force; - }; - force.distance = force.linkDistance; - force.linkStrength = function(x) { - if (!arguments.length) return linkStrength; - linkStrength = typeof x === "function" ? x : +x; - return force; - }; - force.friction = function(x) { - if (!arguments.length) return friction; - friction = +x; - return force; - }; - force.charge = function(x) { - if (!arguments.length) return charge; - charge = typeof x === "function" ? x : +x; - return force; - }; - force.chargeDistance = function(x) { - if (!arguments.length) return Math.sqrt(chargeDistance2); - chargeDistance2 = x * x; - return force; - }; - force.gravity = function(x) { - if (!arguments.length) return gravity; - gravity = +x; - return force; - }; - force.theta = function(x) { - if (!arguments.length) return Math.sqrt(theta2); - theta2 = x * x; - return force; - }; - force.alpha = function(x) { - if (!arguments.length) return alpha; - x = +x; - if (alpha) { - if (x > 0) { - alpha = x; - } else { - timer.c = null, timer.t = NaN, timer = null; - event.end({ - type: "end", - alpha: alpha = 0 - }); - } - } else if (x > 0) { - event.start({ - type: "start", - alpha: alpha = x - }); - timer = d3_timer(force.tick); - } - return force; - }; - force.start = function() { - var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; - for (i = 0; i < n; ++i) { - (o = nodes[i]).index = i; - o.weight = 0; - } - for (i = 0; i < m; ++i) { - o = links[i]; - if (typeof o.source == "number") o.source = nodes[o.source]; - if (typeof o.target == "number") o.target = nodes[o.target]; - ++o.source.weight; - ++o.target.weight; - } - for (i = 0; i < n; ++i) { - o = nodes[i]; - if (isNaN(o.x)) o.x = position("x", w); - if (isNaN(o.y)) o.y = position("y", h); - if (isNaN(o.px)) o.px = o.x; - if (isNaN(o.py)) o.py = o.y; - } - distances = []; - if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; - strengths = []; - if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; - charges = []; - if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; - function position(dimension, size) { - if (!neighbors) { - neighbors = new Array(n); - for (j = 0; j < n; ++j) { - neighbors[j] = []; - } - for (j = 0; j < m; ++j) { - var o = links[j]; - neighbors[o.source.index].push(o.target); - neighbors[o.target.index].push(o.source); - } - } - var candidates = neighbors[i], j = -1, l = candidates.length, x; - while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x; - return Math.random() * size; - } - return force.resume(); - }; - force.resume = function() { - return force.alpha(.1); - }; - force.stop = function() { - return force.alpha(0); - }; - force.drag = function() { - if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); - if (!arguments.length) return drag; - this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); - }; - function dragmove(d) { - d.px = d3.event.x, d.py = d3.event.y; - force.resume(); - } - return d3.rebind(force, event, "on"); - }; - function d3_layout_forceDragstart(d) { - d.fixed |= 2; - } - function d3_layout_forceDragend(d) { - d.fixed &= ~6; - } - function d3_layout_forceMouseover(d) { - d.fixed |= 4; - d.px = d.x, d.py = d.y; - } - function d3_layout_forceMouseout(d) { - d.fixed &= ~4; - } - function d3_layout_forceAccumulate(quad, alpha, charges) { - var cx = 0, cy = 0; - quad.charge = 0; - if (!quad.leaf) { - var nodes = quad.nodes, n = nodes.length, i = -1, c; - while (++i < n) { - c = nodes[i]; - if (c == null) continue; - d3_layout_forceAccumulate(c, alpha, charges); - quad.charge += c.charge; - cx += c.charge * c.cx; - cy += c.charge * c.cy; - } - } - if (quad.point) { - if (!quad.leaf) { - quad.point.x += Math.random() - .5; - quad.point.y += Math.random() - .5; - } - var k = alpha * charges[quad.point.index]; - quad.charge += quad.pointCharge = k; - cx += k * quad.point.x; - cy += k * quad.point.y; - } - quad.cx = cx / quad.charge; - quad.cy = cy / quad.charge; - } - var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; - d3.layout.hierarchy = function() { - var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; - function hierarchy(root) { - var stack = [ root ], nodes = [], node; - root.depth = 0; - while ((node = stack.pop()) != null) { - nodes.push(node); - if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) { - var n, childs, child; - while (--n >= 0) { - stack.push(child = childs[n]); - child.parent = node; - child.depth = node.depth + 1; - } - if (value) node.value = 0; - node.children = childs; - } else { - if (value) node.value = +value.call(hierarchy, node, node.depth) || 0; - delete node.children; - } - } - d3_layout_hierarchyVisitAfter(root, function(node) { - var childs, parent; - if (sort && (childs = node.children)) childs.sort(sort); - if (value && (parent = node.parent)) parent.value += node.value; - }); - return nodes; - } - hierarchy.sort = function(x) { - if (!arguments.length) return sort; - sort = x; - return hierarchy; - }; - hierarchy.children = function(x) { - if (!arguments.length) return children; - children = x; - return hierarchy; - }; - hierarchy.value = function(x) { - if (!arguments.length) return value; - value = x; - return hierarchy; - }; - hierarchy.revalue = function(root) { - if (value) { - d3_layout_hierarchyVisitBefore(root, function(node) { - if (node.children) node.value = 0; - }); - d3_layout_hierarchyVisitAfter(root, function(node) { - var parent; - if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0; - if (parent = node.parent) parent.value += node.value; - }); - } - return root; - }; - return hierarchy; - }; - function d3_layout_hierarchyRebind(object, hierarchy) { - d3.rebind(object, hierarchy, "sort", "children", "value"); - object.nodes = object; - object.links = d3_layout_hierarchyLinks; - return object; - } - function d3_layout_hierarchyVisitBefore(node, callback) { - var nodes = [ node ]; - while ((node = nodes.pop()) != null) { - callback(node); - if ((children = node.children) && (n = children.length)) { - var n, children; - while (--n >= 0) nodes.push(children[n]); - } - } - } - function d3_layout_hierarchyVisitAfter(node, callback) { - var nodes = [ node ], nodes2 = []; - while ((node = nodes.pop()) != null) { - nodes2.push(node); - if ((children = node.children) && (n = children.length)) { - var i = -1, n, children; - while (++i < n) nodes.push(children[i]); - } - } - while ((node = nodes2.pop()) != null) { - callback(node); - } - } - function d3_layout_hierarchyChildren(d) { - return d.children; - } - function d3_layout_hierarchyValue(d) { - return d.value; - } - function d3_layout_hierarchySort(a, b) { - return b.value - a.value; - } - function d3_layout_hierarchyLinks(nodes) { - return d3.merge(nodes.map(function(parent) { - return (parent.children || []).map(function(child) { - return { - source: parent, - target: child - }; - }); - })); - } - d3.layout.partition = function() { - var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; - function position(node, x, dx, dy) { - var children = node.children; - node.x = x; - node.y = node.depth * dy; - node.dx = dx; - node.dy = dy; - if (children && (n = children.length)) { - var i = -1, n, c, d; - dx = node.value ? dx / node.value : 0; - while (++i < n) { - position(c = children[i], x, d = c.value * dx, dy); - x += d; - } - } - } - function depth(node) { - var children = node.children, d = 0; - if (children && (n = children.length)) { - var i = -1, n; - while (++i < n) d = Math.max(d, depth(children[i])); - } - return 1 + d; - } - function partition(d, i) { - var nodes = hierarchy.call(this, d, i); - position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); - return nodes; - } - partition.size = function(x) { - if (!arguments.length) return size; - size = x; - return partition; - }; - return d3_layout_hierarchyRebind(partition, hierarchy); - }; - d3.layout.pie = function() { - var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0; - function pie(data) { - var n = data.length, values = data.map(function(d, i) { - return +value.call(pie, d, i); - }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), sum = d3.sum(values), k = sum ? (da - n * pa) / sum : 0, index = d3.range(n), arcs = [], v; - if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { - return values[j] - values[i]; - } : function(i, j) { - return sort(data[i], data[j]); - }); - index.forEach(function(i) { - arcs[i] = { - data: data[i], - value: v = values[i], - startAngle: a, - endAngle: a += v * k + pa, - padAngle: p - }; - }); - return arcs; - } - pie.value = function(_) { - if (!arguments.length) return value; - value = _; - return pie; - }; - pie.sort = function(_) { - if (!arguments.length) return sort; - sort = _; - return pie; - }; - pie.startAngle = function(_) { - if (!arguments.length) return startAngle; - startAngle = _; - return pie; - }; - pie.endAngle = function(_) { - if (!arguments.length) return endAngle; - endAngle = _; - return pie; - }; - pie.padAngle = function(_) { - if (!arguments.length) return padAngle; - padAngle = _; - return pie; - }; - return pie; - }; - var d3_layout_pieSortByValue = {}; - d3.layout.stack = function() { - var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; - function stack(data, index) { - if (!(n = data.length)) return data; - var series = data.map(function(d, i) { - return values.call(stack, d, i); - }); - var points = series.map(function(d) { - return d.map(function(v, i) { - return [ x.call(stack, v, i), y.call(stack, v, i) ]; - }); - }); - var orders = order.call(stack, points, index); - series = d3.permute(series, orders); - points = d3.permute(points, orders); - var offsets = offset.call(stack, points, index); - var m = series[0].length, n, i, j, o; - for (j = 0; j < m; ++j) { - out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); - for (i = 1; i < n; ++i) { - out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); - } - } - return data; - } - stack.values = function(x) { - if (!arguments.length) return values; - values = x; - return stack; - }; - stack.order = function(x) { - if (!arguments.length) return order; - order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; - return stack; - }; - stack.offset = function(x) { - if (!arguments.length) return offset; - offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; - return stack; - }; - stack.x = function(z) { - if (!arguments.length) return x; - x = z; - return stack; - }; - stack.y = function(z) { - if (!arguments.length) return y; - y = z; - return stack; - }; - stack.out = function(z) { - if (!arguments.length) return out; - out = z; - return stack; - }; - return stack; - }; - function d3_layout_stackX(d) { - return d.x; - } - function d3_layout_stackY(d) { - return d.y; - } - function d3_layout_stackOut(d, y0, y) { - d.y0 = y0; - d.y = y; - } - var d3_layout_stackOrders = d3.map({ - "inside-out": function(data) { - var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { - return max[a] - max[b]; - }), top = 0, bottom = 0, tops = [], bottoms = []; - for (i = 0; i < n; ++i) { - j = index[i]; - if (top < bottom) { - top += sums[j]; - tops.push(j); - } else { - bottom += sums[j]; - bottoms.push(j); - } - } - return bottoms.reverse().concat(tops); - }, - reverse: function(data) { - return d3.range(data.length).reverse(); - }, - "default": d3_layout_stackOrderDefault - }); - var d3_layout_stackOffsets = d3.map({ - silhouette: function(data) { - var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; - for (j = 0; j < m; ++j) { - for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; - if (o > max) max = o; - sums.push(o); - } - for (j = 0; j < m; ++j) { - y0[j] = (max - sums[j]) / 2; - } - return y0; - }, - wiggle: function(data) { - var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; - y0[0] = o = o0 = 0; - for (j = 1; j < m; ++j) { - for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; - for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { - for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { - s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; - } - s2 += s3 * data[i][j][1]; - } - y0[j] = o -= s1 ? s2 / s1 * dx : 0; - if (o < o0) o0 = o; - } - for (j = 0; j < m; ++j) y0[j] -= o0; - return y0; - }, - expand: function(data) { - var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; - for (j = 0; j < m; ++j) { - for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; - if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; - } - for (j = 0; j < m; ++j) y0[j] = 0; - return y0; - }, - zero: d3_layout_stackOffsetZero - }); - function d3_layout_stackOrderDefault(data) { - return d3.range(data.length); - } - function d3_layout_stackOffsetZero(data) { - var j = -1, m = data[0].length, y0 = []; - while (++j < m) y0[j] = 0; - return y0; - } - function d3_layout_stackMaxIndex(array) { - var i = 1, j = 0, v = array[0][1], k, n = array.length; - for (;i < n; ++i) { - if ((k = array[i][1]) > v) { - j = i; - v = k; - } - } - return j; - } - function d3_layout_stackReduceSum(d) { - return d.reduce(d3_layout_stackSum, 0); - } - function d3_layout_stackSum(p, d) { - return p + d[1]; - } - d3.layout.histogram = function() { - var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; - function histogram(data, i) { - var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; - while (++i < m) { - bin = bins[i] = []; - bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); - bin.y = 0; - } - if (m > 0) { - i = -1; - while (++i < n) { - x = values[i]; - if (x >= range[0] && x <= range[1]) { - bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; - bin.y += k; - bin.push(data[i]); - } - } - } - return bins; - } - histogram.value = function(x) { - if (!arguments.length) return valuer; - valuer = x; - return histogram; - }; - histogram.range = function(x) { - if (!arguments.length) return ranger; - ranger = d3_functor(x); - return histogram; - }; - histogram.bins = function(x) { - if (!arguments.length) return binner; - binner = typeof x === "number" ? function(range) { - return d3_layout_histogramBinFixed(range, x); - } : d3_functor(x); - return histogram; - }; - histogram.frequency = function(x) { - if (!arguments.length) return frequency; - frequency = !!x; - return histogram; - }; - return histogram; - }; - function d3_layout_histogramBinSturges(range, values) { - return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); - } - function d3_layout_histogramBinFixed(range, n) { - var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; - while (++x <= n) f[x] = m * x + b; - return f; - } - function d3_layout_histogramRange(values) { - return [ d3.min(values), d3.max(values) ]; - } - d3.layout.pack = function() { - var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; - function pack(d, i) { - var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { - return radius; - }; - root.x = root.y = 0; - d3_layout_hierarchyVisitAfter(root, function(d) { - d.r = +r(d.value); - }); - d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); - if (padding) { - var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; - d3_layout_hierarchyVisitAfter(root, function(d) { - d.r += dr; - }); - d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); - d3_layout_hierarchyVisitAfter(root, function(d) { - d.r -= dr; - }); - } - d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); - return nodes; - } - pack.size = function(_) { - if (!arguments.length) return size; - size = _; - return pack; - }; - pack.radius = function(_) { - if (!arguments.length) return radius; - radius = _ == null || typeof _ === "function" ? _ : +_; - return pack; - }; - pack.padding = function(_) { - if (!arguments.length) return padding; - padding = +_; - return pack; - }; - return d3_layout_hierarchyRebind(pack, hierarchy); - }; - function d3_layout_packSort(a, b) { - return a.value - b.value; - } - function d3_layout_packInsert(a, b) { - var c = a._pack_next; - a._pack_next = b; - b._pack_prev = a; - b._pack_next = c; - c._pack_prev = b; - } - function d3_layout_packSplice(a, b) { - a._pack_next = b; - b._pack_prev = a; - } - function d3_layout_packIntersects(a, b) { - var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; - return .999 * dr * dr > dx * dx + dy * dy; - } - function d3_layout_packSiblings(node) { - if (!(nodes = node.children) || !(n = nodes.length)) return; - var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; - function bound(node) { - xMin = Math.min(node.x - node.r, xMin); - xMax = Math.max(node.x + node.r, xMax); - yMin = Math.min(node.y - node.r, yMin); - yMax = Math.max(node.y + node.r, yMax); - } - nodes.forEach(d3_layout_packLink); - a = nodes[0]; - a.x = -a.r; - a.y = 0; - bound(a); - if (n > 1) { - b = nodes[1]; - b.x = b.r; - b.y = 0; - bound(b); - if (n > 2) { - c = nodes[2]; - d3_layout_packPlace(a, b, c); - bound(c); - d3_layout_packInsert(a, c); - a._pack_prev = c; - d3_layout_packInsert(c, b); - b = a._pack_next; - for (i = 3; i < n; i++) { - d3_layout_packPlace(a, b, c = nodes[i]); - var isect = 0, s1 = 1, s2 = 1; - for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { - if (d3_layout_packIntersects(j, c)) { - isect = 1; - break; - } - } - if (isect == 1) { - for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { - if (d3_layout_packIntersects(k, c)) { - break; - } - } - } - if (isect) { - if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); - i--; - } else { - d3_layout_packInsert(a, c); - b = c; - bound(c); - } - } - } - } - var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; - for (i = 0; i < n; i++) { - c = nodes[i]; - c.x -= cx; - c.y -= cy; - cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); - } - node.r = cr; - nodes.forEach(d3_layout_packUnlink); - } - function d3_layout_packLink(node) { - node._pack_next = node._pack_prev = node; - } - function d3_layout_packUnlink(node) { - delete node._pack_next; - delete node._pack_prev; - } - function d3_layout_packTransform(node, x, y, k) { - var children = node.children; - node.x = x += k * node.x; - node.y = y += k * node.y; - node.r *= k; - if (children) { - var i = -1, n = children.length; - while (++i < n) d3_layout_packTransform(children[i], x, y, k); - } - } - function d3_layout_packPlace(a, b, c) { - var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; - if (db && (dx || dy)) { - var da = b.r + c.r, dc = dx * dx + dy * dy; - da *= da; - db *= db; - var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); - c.x = a.x + x * dx + y * dy; - c.y = a.y + x * dy - y * dx; - } else { - c.x = a.x + db; - c.y = a.y; - } - } - d3.layout.tree = function() { - var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null; - function tree(d, i) { - var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); - d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z; - d3_layout_hierarchyVisitBefore(root1, secondWalk); - if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else { - var left = root0, right = root0, bottom = root0; - d3_layout_hierarchyVisitBefore(root0, function(node) { - if (node.x < left.x) left = node; - if (node.x > right.x) right = node; - if (node.depth > bottom.depth) bottom = node; - }); - var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1); - d3_layout_hierarchyVisitBefore(root0, function(node) { - node.x = (node.x + tx) * kx; - node.y = node.depth * ky; - }); - } - return nodes; - } - function wrapTree(root0) { - var root1 = { - A: null, - children: [ root0 ] - }, queue = [ root1 ], node1; - while ((node1 = queue.pop()) != null) { - for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) { - queue.push((children[i] = child = { - _: children[i], - parent: node1, - children: (child = children[i].children) && child.slice() || [], - A: null, - a: null, - z: 0, - m: 0, - c: 0, - s: 0, - t: null, - i: i - }).a = child); - } - } - return root1.children[0]; - } - function firstWalk(v) { - var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null; - if (children.length) { - d3_layout_treeShift(v); - var midpoint = (children[0].z + children[children.length - 1].z) / 2; - if (w) { - v.z = w.z + separation(v._, w._); - v.m = v.z - midpoint; - } else { - v.z = midpoint; - } - } else if (w) { - v.z = w.z + separation(v._, w._); - } - v.parent.A = apportion(v, w, v.parent.A || siblings[0]); - } - function secondWalk(v) { - v._.x = v.z + v.parent.m; - v.m += v.parent.m; - } - function apportion(v, w, ancestor) { - if (w) { - var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift; - while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { - vom = d3_layout_treeLeft(vom); - vop = d3_layout_treeRight(vop); - vop.a = v; - shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); - if (shift > 0) { - d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift); - sip += shift; - sop += shift; - } - sim += vim.m; - sip += vip.m; - som += vom.m; - sop += vop.m; - } - if (vim && !d3_layout_treeRight(vop)) { - vop.t = vim; - vop.m += sim - sop; - } - if (vip && !d3_layout_treeLeft(vom)) { - vom.t = vip; - vom.m += sip - som; - ancestor = v; - } - } - return ancestor; - } - function sizeNode(node) { - node.x *= size[0]; - node.y = node.depth * size[1]; - } - tree.separation = function(x) { - if (!arguments.length) return separation; - separation = x; - return tree; - }; - tree.size = function(x) { - if (!arguments.length) return nodeSize ? null : size; - nodeSize = (size = x) == null ? sizeNode : null; - return tree; - }; - tree.nodeSize = function(x) { - if (!arguments.length) return nodeSize ? size : null; - nodeSize = (size = x) == null ? null : sizeNode; - return tree; - }; - return d3_layout_hierarchyRebind(tree, hierarchy); - }; - function d3_layout_treeSeparation(a, b) { - return a.parent == b.parent ? 1 : 2; - } - function d3_layout_treeLeft(v) { - var children = v.children; - return children.length ? children[0] : v.t; - } - function d3_layout_treeRight(v) { - var children = v.children, n; - return (n = children.length) ? children[n - 1] : v.t; - } - function d3_layout_treeMove(wm, wp, shift) { - var change = shift / (wp.i - wm.i); - wp.c -= change; - wp.s += shift; - wm.c += change; - wp.z += shift; - wp.m += shift; - } - function d3_layout_treeShift(v) { - var shift = 0, change = 0, children = v.children, i = children.length, w; - while (--i >= 0) { - w = children[i]; - w.z += shift; - w.m += shift; - shift += w.s + (change += w.c); - } - } - function d3_layout_treeAncestor(vim, v, ancestor) { - return vim.a.parent === v.parent ? vim.a : ancestor; - } - d3.layout.cluster = function() { - var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; - function cluster(d, i) { - var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; - d3_layout_hierarchyVisitAfter(root, function(node) { - var children = node.children; - if (children && children.length) { - node.x = d3_layout_clusterX(children); - node.y = d3_layout_clusterY(children); - } else { - node.x = previousNode ? x += separation(node, previousNode) : 0; - node.y = 0; - previousNode = node; - } - }); - var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; - d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) { - node.x = (node.x - root.x) * size[0]; - node.y = (root.y - node.y) * size[1]; - } : function(node) { - node.x = (node.x - x0) / (x1 - x0) * size[0]; - node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; - }); - return nodes; - } - cluster.separation = function(x) { - if (!arguments.length) return separation; - separation = x; - return cluster; - }; - cluster.size = function(x) { - if (!arguments.length) return nodeSize ? null : size; - nodeSize = (size = x) == null; - return cluster; - }; - cluster.nodeSize = function(x) { - if (!arguments.length) return nodeSize ? size : null; - nodeSize = (size = x) != null; - return cluster; - }; - return d3_layout_hierarchyRebind(cluster, hierarchy); - }; - function d3_layout_clusterY(children) { - return 1 + d3.max(children, function(child) { - return child.y; - }); - } - function d3_layout_clusterX(children) { - return children.reduce(function(x, child) { - return x + child.x; - }, 0) / children.length; - } - function d3_layout_clusterLeft(node) { - var children = node.children; - return children && children.length ? d3_layout_clusterLeft(children[0]) : node; - } - function d3_layout_clusterRight(node) { - var children = node.children, n; - return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; - } - d3.layout.treemap = function() { - var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); - function scale(children, k) { - var i = -1, n = children.length, child, area; - while (++i < n) { - area = (child = children[i]).value * (k < 0 ? 0 : k); - child.area = isNaN(area) || area <= 0 ? 0 : area; - } - } - function squarify(node) { - var children = node.children; - if (children && children.length) { - var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; - scale(remaining, rect.dx * rect.dy / node.value); - row.area = 0; - while ((n = remaining.length) > 0) { - row.push(child = remaining[n - 1]); - row.area += child.area; - if (mode !== "squarify" || (score = worst(row, u)) <= best) { - remaining.pop(); - best = score; - } else { - row.area -= row.pop().area; - position(row, u, rect, false); - u = Math.min(rect.dx, rect.dy); - row.length = row.area = 0; - best = Infinity; - } - } - if (row.length) { - position(row, u, rect, true); - row.length = row.area = 0; - } - children.forEach(squarify); - } - } - function stickify(node) { - var children = node.children; - if (children && children.length) { - var rect = pad(node), remaining = children.slice(), child, row = []; - scale(remaining, rect.dx * rect.dy / node.value); - row.area = 0; - while (child = remaining.pop()) { - row.push(child); - row.area += child.area; - if (child.z != null) { - position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); - row.length = row.area = 0; - } - } - children.forEach(stickify); - } - } - function worst(row, u) { - var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; - while (++i < n) { - if (!(r = row[i].area)) continue; - if (r < rmin) rmin = r; - if (r > rmax) rmax = r; - } - s *= s; - u *= u; - return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; - } - function position(row, u, rect, flush) { - var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; - if (u == rect.dx) { - if (flush || v > rect.dy) v = rect.dy; - while (++i < n) { - o = row[i]; - o.x = x; - o.y = y; - o.dy = v; - x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); - } - o.z = true; - o.dx += rect.x + rect.dx - x; - rect.y += v; - rect.dy -= v; - } else { - if (flush || v > rect.dx) v = rect.dx; - while (++i < n) { - o = row[i]; - o.x = x; - o.y = y; - o.dx = v; - y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); - } - o.z = false; - o.dy += rect.y + rect.dy - y; - rect.x += v; - rect.dx -= v; - } - } - function treemap(d) { - var nodes = stickies || hierarchy(d), root = nodes[0]; - root.x = root.y = 0; - if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0; - if (stickies) hierarchy.revalue(root); - scale([ root ], root.dx * root.dy / root.value); - (stickies ? stickify : squarify)(root); - if (sticky) stickies = nodes; - return nodes; - } - treemap.size = function(x) { - if (!arguments.length) return size; - size = x; - return treemap; - }; - treemap.padding = function(x) { - if (!arguments.length) return padding; - function padFunction(node) { - var p = x.call(treemap, node, node.depth); - return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); - } - function padConstant(node) { - return d3_layout_treemapPad(node, x); - } - var type; - pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], - padConstant) : padConstant; - return treemap; - }; - treemap.round = function(x) { - if (!arguments.length) return round != Number; - round = x ? Math.round : Number; - return treemap; - }; - treemap.sticky = function(x) { - if (!arguments.length) return sticky; - sticky = x; - stickies = null; - return treemap; - }; - treemap.ratio = function(x) { - if (!arguments.length) return ratio; - ratio = x; - return treemap; - }; - treemap.mode = function(x) { - if (!arguments.length) return mode; - mode = x + ""; - return treemap; - }; - return d3_layout_hierarchyRebind(treemap, hierarchy); - }; - function d3_layout_treemapPadNull(node) { - return { - x: node.x, - y: node.y, - dx: node.dx, - dy: node.dy - }; - } - function d3_layout_treemapPad(node, padding) { - var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; - if (dx < 0) { - x += dx / 2; - dx = 0; - } - if (dy < 0) { - y += dy / 2; - dy = 0; - } - return { - x: x, - y: y, - dx: dx, - dy: dy - }; - } - d3.random = { - normal: function(µ, σ) { - var n = arguments.length; - if (n < 2) σ = 1; - if (n < 1) µ = 0; - return function() { - var x, y, r; - do { - x = Math.random() * 2 - 1; - y = Math.random() * 2 - 1; - r = x * x + y * y; - } while (!r || r > 1); - return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); - }; - }, - logNormal: function() { - var random = d3.random.normal.apply(d3, arguments); - return function() { - return Math.exp(random()); - }; - }, - bates: function(m) { - var random = d3.random.irwinHall(m); - return function() { - return random() / m; - }; - }, - irwinHall: function(m) { - return function() { - for (var s = 0, j = 0; j < m; j++) s += Math.random(); - return s; - }; - } - }; - d3.scale = {}; - function d3_scaleExtent(domain) { - var start = domain[0], stop = domain[domain.length - 1]; - return start < stop ? [ start, stop ] : [ stop, start ]; - } - function d3_scaleRange(scale) { - return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); - } - function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { - var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); - return function(x) { - return i(u(x)); - }; - } - function d3_scale_nice(domain, nice) { - var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; - if (x1 < x0) { - dx = i0, i0 = i1, i1 = dx; - dx = x0, x0 = x1, x1 = dx; - } - domain[i0] = nice.floor(x0); - domain[i1] = nice.ceil(x1); - return domain; - } - function d3_scale_niceStep(step) { - return step ? { - floor: function(x) { - return Math.floor(x / step) * step; - }, - ceil: function(x) { - return Math.ceil(x / step) * step; - } - } : d3_scale_niceIdentity; - } - var d3_scale_niceIdentity = { - floor: d3_identity, - ceil: d3_identity - }; - function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { - var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; - if (domain[k] < domain[0]) { - domain = domain.slice().reverse(); - range = range.slice().reverse(); - } - while (++j <= k) { - u.push(uninterpolate(domain[j - 1], domain[j])); - i.push(interpolate(range[j - 1], range[j])); - } - return function(x) { - var j = d3.bisect(domain, x, 1, k) - 1; - return i[j](u[j](x)); - }; - } - d3.scale.linear = function() { - return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); - }; - function d3_scale_linear(domain, range, interpolate, clamp) { - var output, input; - function rescale() { - var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; - output = linear(domain, range, uninterpolate, interpolate); - input = linear(range, domain, uninterpolate, d3_interpolate); - return scale; - } - function scale(x) { - return output(x); - } - scale.invert = function(y) { - return input(y); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = x.map(Number); - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.rangeRound = function(x) { - return scale.range(x).interpolate(d3_interpolateRound); - }; - scale.clamp = function(x) { - if (!arguments.length) return clamp; - clamp = x; - return rescale(); - }; - scale.interpolate = function(x) { - if (!arguments.length) return interpolate; - interpolate = x; - return rescale(); - }; - scale.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - scale.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - scale.nice = function(m) { - d3_scale_linearNice(domain, m); - return rescale(); - }; - scale.copy = function() { - return d3_scale_linear(domain, range, interpolate, clamp); - }; - return rescale(); - } - function d3_scale_linearRebind(scale, linear) { - return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); - } - function d3_scale_linearNice(domain, m) { - d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); - d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); - return domain; - } - function d3_scale_linearTickRange(domain, m) { - if (m == null) m = 10; - var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; - if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; - extent[0] = Math.ceil(extent[0] / step) * step; - extent[1] = Math.floor(extent[1] / step) * step + step * .5; - extent[2] = step; - return extent; - } - function d3_scale_linearTicks(domain, m) { - return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); - } - function d3_scale_linearTickFormat(domain, m, format) { - var range = d3_scale_linearTickRange(domain, m); - if (format) { - var match = d3_format_re.exec(format); - match.shift(); - if (match[8] === "s") { - var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); - if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); - match[8] = "f"; - format = d3.format(match.join("")); - return function(d) { - return format(prefix.scale(d)) + prefix.symbol; - }; - } - if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); - format = match.join(""); - } else { - format = ",." + d3_scale_linearPrecision(range[2]) + "f"; - } - return d3.format(format); - } - var d3_scale_linearFormatSignificant = { - s: 1, - g: 1, - p: 1, - r: 1, - e: 1 - }; - function d3_scale_linearPrecision(value) { - return -Math.floor(Math.log(value) / Math.LN10 + .01); - } - function d3_scale_linearFormatPrecision(type, range) { - var p = d3_scale_linearPrecision(range[2]); - return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; - } - d3.scale.log = function() { - return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); - }; - function d3_scale_log(linear, base, positive, domain) { - function log(x) { - return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); - } - function pow(x) { - return positive ? Math.pow(base, x) : -Math.pow(base, -x); - } - function scale(x) { - return linear(log(x)); - } - scale.invert = function(x) { - return pow(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - positive = x[0] >= 0; - linear.domain((domain = x.map(Number)).map(log)); - return scale; - }; - scale.base = function(_) { - if (!arguments.length) return base; - base = +_; - linear.domain(domain.map(log)); - return scale; - }; - scale.nice = function() { - var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); - linear.domain(niced); - domain = niced.map(pow); - return scale; - }; - scale.ticks = function() { - var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; - if (isFinite(j - i)) { - if (positive) { - for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); - ticks.push(pow(i)); - } else { - ticks.push(pow(i)); - for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); - } - for (i = 0; ticks[i] < u; i++) {} - for (j = ticks.length; ticks[j - 1] > v; j--) {} - ticks = ticks.slice(i, j); - } - return ticks; - }; - scale.tickFormat = function(n, format) { - if (!arguments.length) return d3_scale_logFormat; - if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); - var k = Math.max(1, base * n / scale.ticks().length); - return function(d) { - var i = d / pow(Math.round(log(d))); - if (i * base < base - .5) i *= base; - return i <= k ? format(d) : ""; - }; - }; - scale.copy = function() { - return d3_scale_log(linear.copy(), base, positive, domain); - }; - return d3_scale_linearRebind(scale, linear); - } - var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { - floor: function(x) { - return -Math.ceil(-x); - }, - ceil: function(x) { - return -Math.floor(-x); - } - }; - d3.scale.pow = function() { - return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); - }; - function d3_scale_pow(linear, exponent, domain) { - var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); - function scale(x) { - return linear(powp(x)); - } - scale.invert = function(x) { - return powb(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - linear.domain((domain = x.map(Number)).map(powp)); - return scale; - }; - scale.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - scale.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - scale.nice = function(m) { - return scale.domain(d3_scale_linearNice(domain, m)); - }; - scale.exponent = function(x) { - if (!arguments.length) return exponent; - powp = d3_scale_powPow(exponent = x); - powb = d3_scale_powPow(1 / exponent); - linear.domain(domain.map(powp)); - return scale; - }; - scale.copy = function() { - return d3_scale_pow(linear.copy(), exponent, domain); - }; - return d3_scale_linearRebind(scale, linear); - } - function d3_scale_powPow(e) { - return function(x) { - return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); - }; - } - d3.scale.sqrt = function() { - return d3.scale.pow().exponent(.5); - }; - d3.scale.ordinal = function() { - return d3_scale_ordinal([], { - t: "range", - a: [ [] ] - }); - }; - function d3_scale_ordinal(domain, ranger) { - var index, range, rangeBand; - function scale(x) { - return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; - } - function steps(start, step) { - return d3.range(domain.length).map(function(i) { - return start + step * i; - }); - } - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = []; - index = new d3_Map(); - var i = -1, n = x.length, xi; - while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); - return scale[ranger.t].apply(scale, ranger.a); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - rangeBand = 0; - ranger = { - t: "range", - a: arguments - }; - return scale; - }; - scale.rangePoints = function(x, padding) { - if (arguments.length < 2) padding = 0; - var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2, - 0) : (stop - start) / (domain.length - 1 + padding); - range = steps(start + step * padding / 2, step); - rangeBand = 0; - ranger = { - t: "rangePoints", - a: arguments - }; - return scale; - }; - scale.rangeRoundPoints = function(x, padding) { - if (arguments.length < 2) padding = 0; - var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2), - 0) : (stop - start) / (domain.length - 1 + padding) | 0; - range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step); - rangeBand = 0; - ranger = { - t: "rangeRoundPoints", - a: arguments - }; - return scale; - }; - scale.rangeBands = function(x, padding, outerPadding) { - if (arguments.length < 2) padding = 0; - if (arguments.length < 3) outerPadding = padding; - var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); - range = steps(start + step * outerPadding, step); - if (reverse) range.reverse(); - rangeBand = step * (1 - padding); - ranger = { - t: "rangeBands", - a: arguments - }; - return scale; - }; - scale.rangeRoundBands = function(x, padding, outerPadding) { - if (arguments.length < 2) padding = 0; - if (arguments.length < 3) outerPadding = padding; - var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)); - range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step); - if (reverse) range.reverse(); - rangeBand = Math.round(step * (1 - padding)); - ranger = { - t: "rangeRoundBands", - a: arguments - }; - return scale; - }; - scale.rangeBand = function() { - return rangeBand; - }; - scale.rangeExtent = function() { - return d3_scaleExtent(ranger.a[0]); - }; - scale.copy = function() { - return d3_scale_ordinal(domain, ranger); - }; - return scale.domain(domain); - } - d3.scale.category10 = function() { - return d3.scale.ordinal().range(d3_category10); - }; - d3.scale.category20 = function() { - return d3.scale.ordinal().range(d3_category20); - }; - d3.scale.category20b = function() { - return d3.scale.ordinal().range(d3_category20b); - }; - d3.scale.category20c = function() { - return d3.scale.ordinal().range(d3_category20c); - }; - var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); - var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); - var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); - var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); - d3.scale.quantile = function() { - return d3_scale_quantile([], []); - }; - function d3_scale_quantile(domain, range) { - var thresholds; - function rescale() { - var k = 0, q = range.length; - thresholds = []; - while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); - return scale; - } - function scale(x) { - if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; - } - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending); - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.quantiles = function() { - return thresholds; - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; - }; - scale.copy = function() { - return d3_scale_quantile(domain, range); - }; - return rescale(); - } - d3.scale.quantize = function() { - return d3_scale_quantize(0, 1, [ 0, 1 ]); - }; - function d3_scale_quantize(x0, x1, range) { - var kx, i; - function scale(x) { - return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; - } - function rescale() { - kx = range.length / (x1 - x0); - i = range.length - 1; - return scale; - } - scale.domain = function(x) { - if (!arguments.length) return [ x0, x1 ]; - x0 = +x[0]; - x1 = +x[x.length - 1]; - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - y = y < 0 ? NaN : y / kx + x0; - return [ y, y + 1 / kx ]; - }; - scale.copy = function() { - return d3_scale_quantize(x0, x1, range); - }; - return rescale(); - } - d3.scale.threshold = function() { - return d3_scale_threshold([ .5 ], [ 0, 1 ]); - }; - function d3_scale_threshold(domain, range) { - function scale(x) { - if (x <= x) return range[d3.bisect(domain, x)]; - } - scale.domain = function(_) { - if (!arguments.length) return domain; - domain = _; - return scale; - }; - scale.range = function(_) { - if (!arguments.length) return range; - range = _; - return scale; - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - return [ domain[y - 1], domain[y] ]; - }; - scale.copy = function() { - return d3_scale_threshold(domain, range); - }; - return scale; - } - d3.scale.identity = function() { - return d3_scale_identity([ 0, 1 ]); - }; - function d3_scale_identity(domain) { - function identity(x) { - return +x; - } - identity.invert = identity; - identity.domain = identity.range = function(x) { - if (!arguments.length) return domain; - domain = x.map(identity); - return identity; - }; - identity.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - identity.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - identity.copy = function() { - return d3_scale_identity(domain); - }; - return identity; - } - d3.svg = {}; - function d3_zero() { - return 0; - } - d3.svg.arc = function() { - var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle; - function arc() { - var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfπ, a1 = endAngle.apply(this, arguments) - halfπ, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1; - if (r1 < r0) rc = r1, r1 = r0, r0 = rc; - if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z"; - var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = []; - if (ap = (+padAngle.apply(this, arguments) || 0) / 2) { - rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments); - if (!cw) p1 *= -1; - if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap)); - if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap)); - } - if (r1) { - x0 = r1 * Math.cos(a0 + p1); - y0 = r1 * Math.sin(a0 + p1); - x1 = r1 * Math.cos(a1 - p1); - y1 = r1 * Math.sin(a1 - p1); - var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1; - if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) { - var h1 = (a0 + a1) / 2; - x0 = r1 * Math.cos(h1); - y0 = r1 * Math.sin(h1); - x1 = y1 = null; - } - } else { - x0 = y0 = 0; - } - if (r0) { - x2 = r0 * Math.cos(a1 - p0); - y2 = r0 * Math.sin(a1 - p0); - x3 = r0 * Math.cos(a0 + p0); - y3 = r0 * Math.sin(a0 + p0); - var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1; - if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) { - var h0 = (a0 + a1) / 2; - x2 = r0 * Math.cos(h0); - y2 = r0 * Math.sin(h0); - x3 = y3 = null; - } - } else { - x2 = y2 = 0; - } - if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) { - cr = r0 < r1 ^ cw ? 0 : 1; - var rc1 = rc, rc0 = rc; - if (da < π) { - var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); - rc0 = Math.min(rc, (r0 - lc) / (kc - 1)); - rc1 = Math.min(rc, (r1 - lc) / (kc + 1)); - } - if (x1 != null) { - var t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw); - if (rc === rc1) { - path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]); - } else { - path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]); - } - } else { - path.push("M", x0, ",", y0); - } - if (x3 != null) { - var t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw); - if (rc === rc0) { - path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); - } else { - path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); - } - } else { - path.push("L", x2, ",", y2); - } - } else { - path.push("M", x0, ",", y0); - if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1); - path.push("L", x2, ",", y2); - if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3); - } - path.push("Z"); - return path.join(""); - } - function circleSegment(r1, cw) { - return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1; - } - arc.innerRadius = function(v) { - if (!arguments.length) return innerRadius; - innerRadius = d3_functor(v); - return arc; - }; - arc.outerRadius = function(v) { - if (!arguments.length) return outerRadius; - outerRadius = d3_functor(v); - return arc; - }; - arc.cornerRadius = function(v) { - if (!arguments.length) return cornerRadius; - cornerRadius = d3_functor(v); - return arc; - }; - arc.padRadius = function(v) { - if (!arguments.length) return padRadius; - padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v); - return arc; - }; - arc.startAngle = function(v) { - if (!arguments.length) return startAngle; - startAngle = d3_functor(v); - return arc; - }; - arc.endAngle = function(v) { - if (!arguments.length) return endAngle; - endAngle = d3_functor(v); - return arc; - }; - arc.padAngle = function(v) { - if (!arguments.length) return padAngle; - padAngle = d3_functor(v); - return arc; - }; - arc.centroid = function() { - var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ; - return [ Math.cos(a) * r, Math.sin(a) * r ]; - }; - return arc; - }; - var d3_svg_arcAuto = "auto"; - function d3_svg_arcInnerRadius(d) { - return d.innerRadius; - } - function d3_svg_arcOuterRadius(d) { - return d.outerRadius; - } - function d3_svg_arcStartAngle(d) { - return d.startAngle; - } - function d3_svg_arcEndAngle(d) { - return d.endAngle; - } - function d3_svg_arcPadAngle(d) { - return d && d.padAngle; - } - function d3_svg_arcSweep(x0, y0, x1, y1) { - return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1; - } - function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) { - var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3; - if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; - return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ]; - } - function d3_svg_line(projection) { - var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; - function line(data) { - var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); - function segment() { - segments.push("M", interpolate(projection(points), tension)); - } - while (++i < n) { - if (defined.call(this, d = data[i], i)) { - points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); - } else if (points.length) { - segment(); - points = []; - } - } - if (points.length) segment(); - return segments.length ? segments.join("") : null; - } - line.x = function(_) { - if (!arguments.length) return x; - x = _; - return line; - }; - line.y = function(_) { - if (!arguments.length) return y; - y = _; - return line; - }; - line.defined = function(_) { - if (!arguments.length) return defined; - defined = _; - return line; - }; - line.interpolate = function(_) { - if (!arguments.length) return interpolateKey; - if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; - return line; - }; - line.tension = function(_) { - if (!arguments.length) return tension; - tension = _; - return line; - }; - return line; - } - d3.svg.line = function() { - return d3_svg_line(d3_identity); - }; - var d3_svg_lineInterpolators = d3.map({ - linear: d3_svg_lineLinear, - "linear-closed": d3_svg_lineLinearClosed, - step: d3_svg_lineStep, - "step-before": d3_svg_lineStepBefore, - "step-after": d3_svg_lineStepAfter, - basis: d3_svg_lineBasis, - "basis-open": d3_svg_lineBasisOpen, - "basis-closed": d3_svg_lineBasisClosed, - bundle: d3_svg_lineBundle, - cardinal: d3_svg_lineCardinal, - "cardinal-open": d3_svg_lineCardinalOpen, - "cardinal-closed": d3_svg_lineCardinalClosed, - monotone: d3_svg_lineMonotone - }); - d3_svg_lineInterpolators.forEach(function(key, value) { - value.key = key; - value.closed = /-closed$/.test(key); - }); - function d3_svg_lineLinear(points) { - return points.length > 1 ? points.join("L") : points + "Z"; - } - function d3_svg_lineLinearClosed(points) { - return points.join("L") + "Z"; - } - function d3_svg_lineStep(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); - if (n > 1) path.push("H", p[0]); - return path.join(""); - } - function d3_svg_lineStepBefore(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); - return path.join(""); - } - function d3_svg_lineStepAfter(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); - return path.join(""); - } - function d3_svg_lineCardinalOpen(points, tension) { - return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension)); - } - function d3_svg_lineCardinalClosed(points, tension) { - return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), - points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); - } - function d3_svg_lineCardinal(points, tension) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); - } - function d3_svg_lineHermite(points, tangents) { - if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { - return d3_svg_lineLinear(points); - } - var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; - if (quad) { - path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; - p0 = points[1]; - pi = 2; - } - if (tangents.length > 1) { - t = tangents[1]; - p = points[pi]; - pi++; - path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; - for (var i = 2; i < tangents.length; i++, pi++) { - p = points[pi]; - t = tangents[i]; - path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; - } - } - if (quad) { - var lp = points[pi]; - path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; - } - return path; - } - function d3_svg_lineCardinalTangents(points, tension) { - var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; - while (++i < n) { - p0 = p1; - p1 = p2; - p2 = points[i]; - tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); - } - return tangents; - } - function d3_svg_lineBasis(points) { - if (points.length < 3) return d3_svg_lineLinear(points); - var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; - points.push(points[n - 1]); - while (++i <= n) { - pi = points[i]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - points.pop(); - path.push("L", pi); - return path.join(""); - } - function d3_svg_lineBasisOpen(points) { - if (points.length < 4) return d3_svg_lineLinear(points); - var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; - while (++i < 3) { - pi = points[i]; - px.push(pi[0]); - py.push(pi[1]); - } - path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); - --i; - while (++i < n) { - pi = points[i]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - return path.join(""); - } - function d3_svg_lineBasisClosed(points) { - var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; - while (++i < 4) { - pi = points[i % n]; - px.push(pi[0]); - py.push(pi[1]); - } - path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; - --i; - while (++i < m) { - pi = points[i % n]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - return path.join(""); - } - function d3_svg_lineBundle(points, tension) { - var n = points.length - 1; - if (n) { - var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; - while (++i <= n) { - p = points[i]; - t = i / n; - p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); - p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); - } - } - return d3_svg_lineBasis(points); - } - function d3_svg_lineDot4(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; - } - var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; - function d3_svg_lineBasisBezier(path, x, y) { - path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); - } - function d3_svg_lineSlope(p0, p1) { - return (p1[1] - p0[1]) / (p1[0] - p0[0]); - } - function d3_svg_lineFiniteDifferences(points) { - var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); - while (++i < j) { - m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; - } - m[i] = d; - return m; - } - function d3_svg_lineMonotoneTangents(points) { - var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; - while (++i < j) { - d = d3_svg_lineSlope(points[i], points[i + 1]); - if (abs(d) < ε) { - m[i] = m[i + 1] = 0; - } else { - a = m[i] / d; - b = m[i + 1] / d; - s = a * a + b * b; - if (s > 9) { - s = d * 3 / Math.sqrt(s); - m[i] = s * a; - m[i + 1] = s * b; - } - } - } - i = -1; - while (++i <= j) { - s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); - tangents.push([ s || 0, m[i] * s || 0 ]); - } - return tangents; - } - function d3_svg_lineMonotone(points) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); - } - d3.svg.line.radial = function() { - var line = d3_svg_line(d3_svg_lineRadial); - line.radius = line.x, delete line.x; - line.angle = line.y, delete line.y; - return line; - }; - function d3_svg_lineRadial(points) { - var point, i = -1, n = points.length, r, a; - while (++i < n) { - point = points[i]; - r = point[0]; - a = point[1] - halfπ; - point[0] = r * Math.cos(a); - point[1] = r * Math.sin(a); - } - return points; - } - function d3_svg_area(projection) { - var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; - function area(data) { - var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { - return x; - } : d3_functor(x1), fy1 = y0 === y1 ? function() { - return y; - } : d3_functor(y1), x, y; - function segment() { - segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); - } - while (++i < n) { - if (defined.call(this, d = data[i], i)) { - points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); - points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); - } else if (points0.length) { - segment(); - points0 = []; - points1 = []; - } - } - if (points0.length) segment(); - return segments.length ? segments.join("") : null; - } - area.x = function(_) { - if (!arguments.length) return x1; - x0 = x1 = _; - return area; - }; - area.x0 = function(_) { - if (!arguments.length) return x0; - x0 = _; - return area; - }; - area.x1 = function(_) { - if (!arguments.length) return x1; - x1 = _; - return area; - }; - area.y = function(_) { - if (!arguments.length) return y1; - y0 = y1 = _; - return area; - }; - area.y0 = function(_) { - if (!arguments.length) return y0; - y0 = _; - return area; - }; - area.y1 = function(_) { - if (!arguments.length) return y1; - y1 = _; - return area; - }; - area.defined = function(_) { - if (!arguments.length) return defined; - defined = _; - return area; - }; - area.interpolate = function(_) { - if (!arguments.length) return interpolateKey; - if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; - interpolateReverse = interpolate.reverse || interpolate; - L = interpolate.closed ? "M" : "L"; - return area; - }; - area.tension = function(_) { - if (!arguments.length) return tension; - tension = _; - return area; - }; - return area; - } - d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; - d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; - d3.svg.area = function() { - return d3_svg_area(d3_identity); - }; - d3.svg.area.radial = function() { - var area = d3_svg_area(d3_svg_lineRadial); - area.radius = area.x, delete area.x; - area.innerRadius = area.x0, delete area.x0; - area.outerRadius = area.x1, delete area.x1; - area.angle = area.y, delete area.y; - area.startAngle = area.y0, delete area.y0; - area.endAngle = area.y1, delete area.y1; - return area; - }; - d3.svg.chord = function() { - var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; - function chord(d, i) { - var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); - return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; - } - function subgroup(self, f, d, i) { - var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfπ, a1 = endAngle.call(self, subgroup, i) - halfπ; - return { - r: r, - a0: a0, - a1: a1, - p0: [ r * Math.cos(a0), r * Math.sin(a0) ], - p1: [ r * Math.cos(a1), r * Math.sin(a1) ] - }; - } - function equals(a, b) { - return a.a0 == b.a0 && a.a1 == b.a1; - } - function arc(r, p, a) { - return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; - } - function curve(r0, p0, r1, p1) { - return "Q 0,0 " + p1; - } - chord.radius = function(v) { - if (!arguments.length) return radius; - radius = d3_functor(v); - return chord; - }; - chord.source = function(v) { - if (!arguments.length) return source; - source = d3_functor(v); - return chord; - }; - chord.target = function(v) { - if (!arguments.length) return target; - target = d3_functor(v); - return chord; - }; - chord.startAngle = function(v) { - if (!arguments.length) return startAngle; - startAngle = d3_functor(v); - return chord; - }; - chord.endAngle = function(v) { - if (!arguments.length) return endAngle; - endAngle = d3_functor(v); - return chord; - }; - return chord; - }; - function d3_svg_chordRadius(d) { - return d.radius; - } - d3.svg.diagonal = function() { - var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; - function diagonal(d, i) { - var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { - x: p0.x, - y: m - }, { - x: p3.x, - y: m - }, p3 ]; - p = p.map(projection); - return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; - } - diagonal.source = function(x) { - if (!arguments.length) return source; - source = d3_functor(x); - return diagonal; - }; - diagonal.target = function(x) { - if (!arguments.length) return target; - target = d3_functor(x); - return diagonal; - }; - diagonal.projection = function(x) { - if (!arguments.length) return projection; - projection = x; - return diagonal; - }; - return diagonal; - }; - function d3_svg_diagonalProjection(d) { - return [ d.x, d.y ]; - } - d3.svg.diagonal.radial = function() { - var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; - diagonal.projection = function(x) { - return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; - }; - return diagonal; - }; - function d3_svg_diagonalRadialProjection(projection) { - return function() { - var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ; - return [ r * Math.cos(a), r * Math.sin(a) ]; - }; - } - d3.svg.symbol = function() { - var type = d3_svg_symbolType, size = d3_svg_symbolSize; - function symbol(d, i) { - return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); - } - symbol.type = function(x) { - if (!arguments.length) return type; - type = d3_functor(x); - return symbol; - }; - symbol.size = function(x) { - if (!arguments.length) return size; - size = d3_functor(x); - return symbol; - }; - return symbol; - }; - function d3_svg_symbolSize() { - return 64; - } - function d3_svg_symbolType() { - return "circle"; - } - function d3_svg_symbolCircle(size) { - var r = Math.sqrt(size / π); - return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; - } - var d3_svg_symbols = d3.map({ - circle: d3_svg_symbolCircle, - cross: function(size) { - var r = Math.sqrt(size / 5) / 2; - return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; - }, - diamond: function(size) { - var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; - return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; - }, - square: function(size) { - var r = Math.sqrt(size) / 2; - return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; - }, - "triangle-down": function(size) { - var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; - return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; - }, - "triangle-up": function(size) { - var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; - return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; - } - }); - d3.svg.symbolTypes = d3_svg_symbols.keys(); - var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); - d3_selectionPrototype.transition = function(name) { - var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || { - time: Date.now(), - ease: d3_ease_cubicInOut, - delay: 0, - duration: 250 - }; - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) d3_transitionNode(node, i, ns, id, transition); - subgroup.push(node); - } - } - return d3_transition(subgroups, ns, id); - }; - d3_selectionPrototype.interrupt = function(name) { - return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name))); - }; - var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace()); - function d3_selection_interruptNS(ns) { - return function() { - var lock, activeId, active; - if ((lock = this[ns]) && (active = lock[activeId = lock.active])) { - active.timer.c = null; - active.timer.t = NaN; - if (--lock.count) delete lock[activeId]; else delete this[ns]; - lock.active += .5; - active.event && active.event.interrupt.call(this, this.__data__, active.index); - } - }; - } - function d3_transition(groups, ns, id) { - d3_subclass(groups, d3_transitionPrototype); - groups.namespace = ns; - groups.id = id; - return groups; - } - var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; - d3_transitionPrototype.call = d3_selectionPrototype.call; - d3_transitionPrototype.empty = d3_selectionPrototype.empty; - d3_transitionPrototype.node = d3_selectionPrototype.node; - d3_transitionPrototype.size = d3_selectionPrototype.size; - d3.transition = function(selection, name) { - return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection); - }; - d3.transition.prototype = d3_transitionPrototype; - d3_transitionPrototype.select = function(selector) { - var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node; - selector = d3_selection_selector(selector); - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - d3_transitionNode(subnode, i, ns, id, node[ns][id]); - subgroup.push(subnode); - } else { - subgroup.push(null); - } - } - } - return d3_transition(subgroups, ns, id); - }; - d3_transitionPrototype.selectAll = function(selector) { - var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition; - selector = d3_selection_selectorAll(selector); - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - transition = node[ns][id]; - subnodes = selector.call(node, node.__data__, i, j); - subgroups.push(subgroup = []); - for (var k = -1, o = subnodes.length; ++k < o; ) { - if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition); - subgroup.push(subnode); - } - } - } - } - return d3_transition(subgroups, ns, id); - }; - d3_transitionPrototype.filter = function(filter) { - var subgroups = [], subgroup, group, node; - if (typeof filter !== "function") filter = d3_selection_filter(filter); - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { - subgroup.push(node); - } - } - } - return d3_transition(subgroups, this.namespace, this.id); - }; - d3_transitionPrototype.tween = function(name, tween) { - var id = this.id, ns = this.namespace; - if (arguments.length < 2) return this.node()[ns][id].tween.get(name); - return d3_selection_each(this, tween == null ? function(node) { - node[ns][id].tween.remove(name); - } : function(node) { - node[ns][id].tween.set(name, tween); - }); - }; - function d3_transition_tween(groups, name, value, tween) { - var id = groups.id, ns = groups.namespace; - return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { - node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); - } : (value = tween(value), function(node) { - node[ns][id].tween.set(name, value); - })); - } - d3_transitionPrototype.attr = function(nameNS, value) { - if (arguments.length < 2) { - for (value in nameNS) this.attr(value, nameNS[value]); - return this; - } - var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); - function attrNull() { - this.removeAttribute(name); - } - function attrNullNS() { - this.removeAttributeNS(name.space, name.local); - } - function attrTween(b) { - return b == null ? attrNull : (b += "", function() { - var a = this.getAttribute(name), i; - return a !== b && (i = interpolate(a, b), function(t) { - this.setAttribute(name, i(t)); - }); - }); - } - function attrTweenNS(b) { - return b == null ? attrNullNS : (b += "", function() { - var a = this.getAttributeNS(name.space, name.local), i; - return a !== b && (i = interpolate(a, b), function(t) { - this.setAttributeNS(name.space, name.local, i(t)); - }); - }); - } - return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); - }; - d3_transitionPrototype.attrTween = function(nameNS, tween) { - var name = d3.ns.qualify(nameNS); - function attrTween(d, i) { - var f = tween.call(this, d, i, this.getAttribute(name)); - return f && function(t) { - this.setAttribute(name, f(t)); - }; - } - function attrTweenNS(d, i) { - var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); - return f && function(t) { - this.setAttributeNS(name.space, name.local, f(t)); - }; - } - return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); - }; - d3_transitionPrototype.style = function(name, value, priority) { - var n = arguments.length; - if (n < 3) { - if (typeof name !== "string") { - if (n < 2) value = ""; - for (priority in name) this.style(priority, name[priority], value); - return this; - } - priority = ""; - } - function styleNull() { - this.style.removeProperty(name); - } - function styleString(b) { - return b == null ? styleNull : (b += "", function() { - var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i; - return a !== b && (i = d3_interpolate(a, b), function(t) { - this.style.setProperty(name, i(t), priority); - }); - }); - } - return d3_transition_tween(this, "style." + name, value, styleString); - }; - d3_transitionPrototype.styleTween = function(name, tween, priority) { - if (arguments.length < 3) priority = ""; - function styleTween(d, i) { - var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name)); - return f && function(t) { - this.style.setProperty(name, f(t), priority); - }; - } - return this.tween("style." + name, styleTween); - }; - d3_transitionPrototype.text = function(value) { - return d3_transition_tween(this, "text", value, d3_transition_text); - }; - function d3_transition_text(b) { - if (b == null) b = ""; - return function() { - this.textContent = b; - }; - } - d3_transitionPrototype.remove = function() { - var ns = this.namespace; - return this.each("end.transition", function() { - var p; - if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this); - }); - }; - d3_transitionPrototype.ease = function(value) { - var id = this.id, ns = this.namespace; - if (arguments.length < 1) return this.node()[ns][id].ease; - if (typeof value !== "function") value = d3.ease.apply(d3, arguments); - return d3_selection_each(this, function(node) { - node[ns][id].ease = value; - }); - }; - d3_transitionPrototype.delay = function(value) { - var id = this.id, ns = this.namespace; - if (arguments.length < 1) return this.node()[ns][id].delay; - return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { - node[ns][id].delay = +value.call(node, node.__data__, i, j); - } : (value = +value, function(node) { - node[ns][id].delay = value; - })); - }; - d3_transitionPrototype.duration = function(value) { - var id = this.id, ns = this.namespace; - if (arguments.length < 1) return this.node()[ns][id].duration; - return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { - node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); - } : (value = Math.max(1, value), function(node) { - node[ns][id].duration = value; - })); - }; - d3_transitionPrototype.each = function(type, listener) { - var id = this.id, ns = this.namespace; - if (arguments.length < 2) { - var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; - try { - d3_transitionInheritId = id; - d3_selection_each(this, function(node, i, j) { - d3_transitionInherit = node[ns][id]; - type.call(node, node.__data__, i, j); - }); - } finally { - d3_transitionInherit = inherit; - d3_transitionInheritId = inheritId; - } - } else { - d3_selection_each(this, function(node) { - var transition = node[ns][id]; - (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener); - }); - } - return this; - }; - d3_transitionPrototype.transition = function() { - var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition; - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if (node = group[i]) { - transition = node[ns][id0]; - d3_transitionNode(node, i, ns, id1, { - time: transition.time, - ease: transition.ease, - delay: transition.delay + transition.duration, - duration: transition.duration - }); - } - subgroup.push(node); - } - } - return d3_transition(subgroups, ns, id1); - }; - function d3_transitionNamespace(name) { - return name == null ? "__transition__" : "__transition_" + name + "__"; - } - function d3_transitionNode(node, i, ns, id, inherit) { - var lock = node[ns] || (node[ns] = { - active: 0, - count: 0 - }), transition = lock[id], time, timer, duration, ease, tweens; - function schedule(elapsed) { - var delay = transition.delay; - timer.t = delay + time; - if (delay <= elapsed) return start(elapsed - delay); - timer.c = start; - } - function start(elapsed) { - var activeId = lock.active, active = lock[activeId]; - if (active) { - active.timer.c = null; - active.timer.t = NaN; - --lock.count; - delete lock[activeId]; - active.event && active.event.interrupt.call(node, node.__data__, active.index); - } - for (var cancelId in lock) { - if (+cancelId < id) { - var cancel = lock[cancelId]; - cancel.timer.c = null; - cancel.timer.t = NaN; - --lock.count; - delete lock[cancelId]; - } - } - timer.c = tick; - d3_timer(function() { - if (timer.c && tick(elapsed || 1)) { - timer.c = null; - timer.t = NaN; - } - return 1; - }, 0, time); - lock.active = id; - transition.event && transition.event.start.call(node, node.__data__, i); - tweens = []; - transition.tween.forEach(function(key, value) { - if (value = value.call(node, node.__data__, i)) { - tweens.push(value); - } - }); - ease = transition.ease; - duration = transition.duration; - } - function tick(elapsed) { - var t = elapsed / duration, e = ease(t), n = tweens.length; - while (n > 0) { - tweens[--n].call(node, e); - } - if (t >= 1) { - transition.event && transition.event.end.call(node, node.__data__, i); - if (--lock.count) delete lock[id]; else delete node[ns]; - return 1; - } - } - if (!transition) { - time = inherit.time; - timer = d3_timer(schedule, 0, time); - transition = lock[id] = { - tween: new d3_Map(), - time: time, - timer: timer, - delay: inherit.delay, - duration: inherit.duration, - ease: inherit.ease, - index: i - }; - inherit = null; - ++lock.count; - } - } - d3.svg.axis = function() { - var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; - function axis(g) { - g.each(function() { - var g = d3.select(this); - var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); - var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform; - var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), - d3.transition(path)); - tickEnter.append("line"); - tickEnter.append("text"); - var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2; - if (orient === "bottom" || orient === "top") { - tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2"; - text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle"); - pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize); - } else { - tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2"; - text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start"); - pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize); - } - lineEnter.attr(y2, sign * innerTickSize); - textEnter.attr(y1, sign * tickSpacing); - lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize); - textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing); - if (scale1.rangeBand) { - var x = scale1, dx = x.rangeBand() / 2; - scale0 = scale1 = function(d) { - return x(d) + dx; - }; - } else if (scale0.rangeBand) { - scale0 = scale1; - } else { - tickExit.call(tickTransform, scale1, scale0); - } - tickEnter.call(tickTransform, scale0, scale1); - tickUpdate.call(tickTransform, scale1, scale1); - }); - } - axis.scale = function(x) { - if (!arguments.length) return scale; - scale = x; - return axis; - }; - axis.orient = function(x) { - if (!arguments.length) return orient; - orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; - return axis; - }; - axis.ticks = function() { - if (!arguments.length) return tickArguments_; - tickArguments_ = d3_array(arguments); - return axis; - }; - axis.tickValues = function(x) { - if (!arguments.length) return tickValues; - tickValues = x; - return axis; - }; - axis.tickFormat = function(x) { - if (!arguments.length) return tickFormat_; - tickFormat_ = x; - return axis; - }; - axis.tickSize = function(x) { - var n = arguments.length; - if (!n) return innerTickSize; - innerTickSize = +x; - outerTickSize = +arguments[n - 1]; - return axis; - }; - axis.innerTickSize = function(x) { - if (!arguments.length) return innerTickSize; - innerTickSize = +x; - return axis; - }; - axis.outerTickSize = function(x) { - if (!arguments.length) return outerTickSize; - outerTickSize = +x; - return axis; - }; - axis.tickPadding = function(x) { - if (!arguments.length) return tickPadding; - tickPadding = +x; - return axis; - }; - axis.tickSubdivide = function() { - return arguments.length && axis; - }; - return axis; - }; - var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { - top: 1, - right: 1, - bottom: 1, - left: 1 - }; - function d3_svg_axisX(selection, x0, x1) { - selection.attr("transform", function(d) { - var v0 = x0(d); - return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)"; - }); - } - function d3_svg_axisY(selection, y0, y1) { - selection.attr("transform", function(d) { - var v0 = y0(d); - return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")"; - }); - } - d3.svg.brush = function() { - var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; - function brush(g) { - g.each(function() { - var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); - var background = g.selectAll(".background").data([ 0 ]); - background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); - g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); - var resize = g.selectAll(".resize").data(resizes, d3_identity); - resize.exit().remove(); - resize.enter().append("g").attr("class", function(d) { - return "resize " + d; - }).style("cursor", function(d) { - return d3_svg_brushCursor[d]; - }).append("rect").attr("x", function(d) { - return /[ew]$/.test(d) ? -3 : null; - }).attr("y", function(d) { - return /^[ns]/.test(d) ? -3 : null; - }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); - resize.style("display", brush.empty() ? "none" : null); - var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; - if (x) { - range = d3_scaleRange(x); - backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); - redrawX(gUpdate); - } - if (y) { - range = d3_scaleRange(y); - backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); - redrawY(gUpdate); - } - redraw(gUpdate); - }); - } - brush.event = function(g) { - g.each(function() { - var event_ = event.of(this, arguments), extent1 = { - x: xExtent, - y: yExtent, - i: xExtentDomain, - j: yExtentDomain - }, extent0 = this.__chart__ || extent1; - this.__chart__ = extent1; - if (d3_transitionInheritId) { - d3.select(this).transition().each("start.brush", function() { - xExtentDomain = extent0.i; - yExtentDomain = extent0.j; - xExtent = extent0.x; - yExtent = extent0.y; - event_({ - type: "brushstart" - }); - }).tween("brush:brush", function() { - var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); - xExtentDomain = yExtentDomain = null; - return function(t) { - xExtent = extent1.x = xi(t); - yExtent = extent1.y = yi(t); - event_({ - type: "brush", - mode: "resize" - }); - }; - }).each("end.brush", function() { - xExtentDomain = extent1.i; - yExtentDomain = extent1.j; - event_({ - type: "brush", - mode: "resize" - }); - event_({ - type: "brushend" - }); - }); - } else { - event_({ - type: "brushstart" - }); - event_({ - type: "brush", - mode: "resize" - }); - event_({ - type: "brushend" - }); - } - }); - }; - function redraw(g) { - g.selectAll(".resize").attr("transform", function(d) { - return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; - }); - } - function redrawX(g) { - g.select(".extent").attr("x", xExtent[0]); - g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); - } - function redrawY(g) { - g.select(".extent").attr("y", yExtent[0]); - g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); - } - function brushstart() { - var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(target), center, origin = d3.mouse(target), offset; - var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup); - if (d3.event.changedTouches) { - w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); - } else { - w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); - } - g.interrupt().selectAll("*").interrupt(); - if (dragging) { - origin[0] = xExtent[0] - origin[0]; - origin[1] = yExtent[0] - origin[1]; - } else if (resizing) { - var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); - offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; - origin[0] = xExtent[ex]; - origin[1] = yExtent[ey]; - } else if (d3.event.altKey) center = origin.slice(); - g.style("pointer-events", "none").selectAll(".resize").style("display", null); - d3.select("body").style("cursor", eventTarget.style("cursor")); - event_({ - type: "brushstart" - }); - brushmove(); - function keydown() { - if (d3.event.keyCode == 32) { - if (!dragging) { - center = null; - origin[0] -= xExtent[1]; - origin[1] -= yExtent[1]; - dragging = 2; - } - d3_eventPreventDefault(); - } - } - function keyup() { - if (d3.event.keyCode == 32 && dragging == 2) { - origin[0] += xExtent[1]; - origin[1] += yExtent[1]; - dragging = 0; - d3_eventPreventDefault(); - } - } - function brushmove() { - var point = d3.mouse(target), moved = false; - if (offset) { - point[0] += offset[0]; - point[1] += offset[1]; - } - if (!dragging) { - if (d3.event.altKey) { - if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; - origin[0] = xExtent[+(point[0] < center[0])]; - origin[1] = yExtent[+(point[1] < center[1])]; - } else center = null; - } - if (resizingX && move1(point, x, 0)) { - redrawX(g); - moved = true; - } - if (resizingY && move1(point, y, 1)) { - redrawY(g); - moved = true; - } - if (moved) { - redraw(g); - event_({ - type: "brush", - mode: dragging ? "move" : "resize" - }); - } - } - function move1(point, scale, i) { - var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; - if (dragging) { - r0 -= position; - r1 -= size + position; - } - min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; - if (dragging) { - max = (min += position) + size; - } else { - if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); - if (position < min) { - max = min; - min = position; - } else { - max = position; - } - } - if (extent[0] != min || extent[1] != max) { - if (i) yExtentDomain = null; else xExtentDomain = null; - extent[0] = min; - extent[1] = max; - return true; - } - } - function brushend() { - brushmove(); - g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); - d3.select("body").style("cursor", null); - w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); - dragRestore(); - event_({ - type: "brushend" - }); - } - } - brush.x = function(z) { - if (!arguments.length) return x; - x = z; - resizes = d3_svg_brushResizes[!x << 1 | !y]; - return brush; - }; - brush.y = function(z) { - if (!arguments.length) return y; - y = z; - resizes = d3_svg_brushResizes[!x << 1 | !y]; - return brush; - }; - brush.clamp = function(z) { - if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; - if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; - return brush; - }; - brush.extent = function(z) { - var x0, x1, y0, y1, t; - if (!arguments.length) { - if (x) { - if (xExtentDomain) { - x0 = xExtentDomain[0], x1 = xExtentDomain[1]; - } else { - x0 = xExtent[0], x1 = xExtent[1]; - if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); - if (x1 < x0) t = x0, x0 = x1, x1 = t; - } - } - if (y) { - if (yExtentDomain) { - y0 = yExtentDomain[0], y1 = yExtentDomain[1]; - } else { - y0 = yExtent[0], y1 = yExtent[1]; - if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); - if (y1 < y0) t = y0, y0 = y1, y1 = t; - } - } - return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; - } - if (x) { - x0 = z[0], x1 = z[1]; - if (y) x0 = x0[0], x1 = x1[0]; - xExtentDomain = [ x0, x1 ]; - if (x.invert) x0 = x(x0), x1 = x(x1); - if (x1 < x0) t = x0, x0 = x1, x1 = t; - if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; - } - if (y) { - y0 = z[0], y1 = z[1]; - if (x) y0 = y0[1], y1 = y1[1]; - yExtentDomain = [ y0, y1 ]; - if (y.invert) y0 = y(y0), y1 = y(y1); - if (y1 < y0) t = y0, y0 = y1, y1 = t; - if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; - } - return brush; - }; - brush.clear = function() { - if (!brush.empty()) { - xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; - xExtentDomain = yExtentDomain = null; - } - return brush; - }; - brush.empty = function() { - return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; - }; - return d3.rebind(brush, event, "on"); - }; - var d3_svg_brushCursor = { - n: "ns-resize", - e: "ew-resize", - s: "ns-resize", - w: "ew-resize", - nw: "nwse-resize", - ne: "nesw-resize", - se: "nwse-resize", - sw: "nesw-resize" - }; - var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; - var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; - var d3_time_formatUtc = d3_time_format.utc; - var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); - d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; - function d3_time_formatIsoNative(date) { - return date.toISOString(); - } - d3_time_formatIsoNative.parse = function(string) { - var date = new Date(string); - return isNaN(date) ? null : date; - }; - d3_time_formatIsoNative.toString = d3_time_formatIso.toString; - d3_time.second = d3_time_interval(function(date) { - return new d3_date(Math.floor(date / 1e3) * 1e3); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 1e3); - }, function(date) { - return date.getSeconds(); - }); - d3_time.seconds = d3_time.second.range; - d3_time.seconds.utc = d3_time.second.utc.range; - d3_time.minute = d3_time_interval(function(date) { - return new d3_date(Math.floor(date / 6e4) * 6e4); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 6e4); - }, function(date) { - return date.getMinutes(); - }); - d3_time.minutes = d3_time.minute.range; - d3_time.minutes.utc = d3_time.minute.utc.range; - d3_time.hour = d3_time_interval(function(date) { - var timezone = date.getTimezoneOffset() / 60; - return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 36e5); - }, function(date) { - return date.getHours(); - }); - d3_time.hours = d3_time.hour.range; - d3_time.hours.utc = d3_time.hour.utc.range; - d3_time.month = d3_time_interval(function(date) { - date = d3_time.day(date); - date.setDate(1); - return date; - }, function(date, offset) { - date.setMonth(date.getMonth() + offset); - }, function(date) { - return date.getMonth(); - }); - d3_time.months = d3_time.month.range; - d3_time.months.utc = d3_time.month.utc.range; - function d3_time_scale(linear, methods, format) { - function scale(x) { - return linear(x); - } - scale.invert = function(x) { - return d3_time_scaleDate(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return linear.domain().map(d3_time_scaleDate); - linear.domain(x); - return scale; - }; - function tickMethod(extent, count) { - var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); - return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { - return d / 31536e6; - }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; - } - scale.nice = function(interval, skip) { - var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); - if (method) interval = method[0], skip = method[1]; - function skipped(date) { - return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; - } - return scale.domain(d3_scale_nice(domain, skip > 1 ? { - floor: function(date) { - while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); - return date; - }, - ceil: function(date) { - while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); - return date; - } - } : interval)); - }; - scale.ticks = function(interval, skip) { - var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { - range: interval - }, skip ]; - if (method) interval = method[0], skip = method[1]; - return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); - }; - scale.tickFormat = function() { - return format; - }; - scale.copy = function() { - return d3_time_scale(linear.copy(), methods, format); - }; - return d3_scale_linearRebind(scale, linear); - } - function d3_time_scaleDate(t) { - return new Date(t); - } - var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; - var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; - var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { - return d.getMilliseconds(); - } ], [ ":%S", function(d) { - return d.getSeconds(); - } ], [ "%I:%M", function(d) { - return d.getMinutes(); - } ], [ "%I %p", function(d) { - return d.getHours(); - } ], [ "%a %d", function(d) { - return d.getDay() && d.getDate() != 1; - } ], [ "%b %d", function(d) { - return d.getDate() != 1; - } ], [ "%B", function(d) { - return d.getMonth(); - } ], [ "%Y", d3_true ] ]); - var d3_time_scaleMilliseconds = { - range: function(start, stop, step) { - return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); - }, - floor: d3_identity, - ceil: d3_identity - }; - d3_time_scaleLocalMethods.year = d3_time.year; - d3_time.scale = function() { - return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); - }; - var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { - return [ m[0].utc, m[1] ]; - }); - var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { - return d.getUTCMilliseconds(); - } ], [ ":%S", function(d) { - return d.getUTCSeconds(); - } ], [ "%I:%M", function(d) { - return d.getUTCMinutes(); - } ], [ "%I %p", function(d) { - return d.getUTCHours(); - } ], [ "%a %d", function(d) { - return d.getUTCDay() && d.getUTCDate() != 1; - } ], [ "%b %d", function(d) { - return d.getUTCDate() != 1; - } ], [ "%B", function(d) { - return d.getUTCMonth(); - } ], [ "%Y", d3_true ] ]); - d3_time_scaleUtcMethods.year = d3_time.year.utc; - d3_time.scale.utc = function() { - return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); - }; - d3.text = d3_xhrType(function(request) { - return request.responseText; - }); - d3.json = function(url, callback) { - return d3_xhr(url, "application/json", d3_json, callback); - }; - function d3_json(request) { - return JSON.parse(request.responseText); - } - d3.html = function(url, callback) { - return d3_xhr(url, "text/html", d3_html, callback); - }; - function d3_html(request) { - var range = d3_document.createRange(); - range.selectNode(d3_document.body); - return range.createContextualFragment(request.responseText); - } - d3.xml = d3_xhrType(function(request) { - return request.responseXML; - }); - if (typeof define === "function" && define.amd) this.d3 = d3, define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; else this.d3 = d3; -}(); \ No newline at end of file diff --git a/debian/missing-sources/d3-4.12.2.js b/debian/missing-sources/d3-4.12.2.js new file mode 100644 index 000000000..bd06c10f4 --- /dev/null +++ b/debian/missing-sources/d3-4.12.2.js @@ -0,0 +1,17160 @@ +// https://d3js.org Version 4.12.2. Copyright 2017 Mike Bostock. +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.d3 = global.d3 || {}))); +}(this, (function (exports) { 'use strict'; + +var version = "4.12.2"; + +function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; +} + +function bisector(compare) { + if (compare.length === 1) compare = ascendingComparator(compare); + return { + left: function(a, x, lo, hi) { + if (lo == null) lo = 0; + if (hi == null) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) < 0) lo = mid + 1; + else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (lo == null) lo = 0; + if (hi == null) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) > 0) hi = mid; + else lo = mid + 1; + } + return lo; + } + }; +} + +function ascendingComparator(f) { + return function(d, x) { + return ascending(f(d), x); + }; +} + +var ascendingBisect = bisector(ascending); +var bisectRight = ascendingBisect.right; +var bisectLeft = ascendingBisect.left; + +function pairs(array, f) { + if (f == null) f = pair; + var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n); + while (i < n) pairs[i] = f(p, p = array[++i]); + return pairs; +} + +function pair(a, b) { + return [a, b]; +} + +function cross(values0, values1, reduce) { + var n0 = values0.length, + n1 = values1.length, + values = new Array(n0 * n1), + i0, + i1, + i, + value0; + + if (reduce == null) reduce = pair; + + for (i0 = i = 0; i0 < n0; ++i0) { + for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) { + values[i] = reduce(value0, values1[i1]); + } + } + + return values; +} + +function descending(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; +} + +function number(x) { + return x === null ? NaN : +x; +} + +function variance(values, valueof) { + var n = values.length, + m = 0, + i = -1, + mean = 0, + value, + delta, + sum = 0; + + if (valueof == null) { + while (++i < n) { + if (!isNaN(value = number(values[i]))) { + delta = value - mean; + mean += delta / ++m; + sum += delta * (value - mean); + } + } + } + + else { + while (++i < n) { + if (!isNaN(value = number(valueof(values[i], i, values)))) { + delta = value - mean; + mean += delta / ++m; + sum += delta * (value - mean); + } + } + } + + if (m > 1) return sum / (m - 1); +} + +function deviation(array, f) { + var v = variance(array, f); + return v ? Math.sqrt(v) : v; +} + +function extent(values, valueof) { + var n = values.length, + i = -1, + value, + min, + max; + + if (valueof == null) { + while (++i < n) { // Find the first comparable value. + if ((value = values[i]) != null && value >= value) { + min = max = value; + while (++i < n) { // Compare the remaining values. + if ((value = values[i]) != null) { + if (min > value) min = value; + if (max < value) max = value; + } + } + } + } + } + + else { + while (++i < n) { // Find the first comparable value. + if ((value = valueof(values[i], i, values)) != null && value >= value) { + min = max = value; + while (++i < n) { // Compare the remaining values. + if ((value = valueof(values[i], i, values)) != null) { + if (min > value) min = value; + if (max < value) max = value; + } + } + } + } + } + + return [min, max]; +} + +var array = Array.prototype; + +var slice = array.slice; +var map = array.map; + +function constant(x) { + return function() { + return x; + }; +} + +function identity(x) { + return x; +} + +function sequence(start, stop, step) { + start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step; + + var i = -1, + n = Math.max(0, Math.ceil((stop - start) / step)) | 0, + range = new Array(n); + + while (++i < n) { + range[i] = start + i * step; + } + + return range; +} + +var e10 = Math.sqrt(50); +var e5 = Math.sqrt(10); +var e2 = Math.sqrt(2); + +function ticks(start, stop, count) { + var reverse, + i = -1, + n, + ticks, + step; + + stop = +stop, start = +start, count = +count; + if (start === stop && count > 0) return [start]; + if (reverse = stop < start) n = start, start = stop, stop = n; + if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return []; + + if (step > 0) { + start = Math.ceil(start / step); + stop = Math.floor(stop / step); + ticks = new Array(n = Math.ceil(stop - start + 1)); + while (++i < n) ticks[i] = (start + i) * step; + } else { + start = Math.floor(start * step); + stop = Math.ceil(stop * step); + ticks = new Array(n = Math.ceil(start - stop + 1)); + while (++i < n) ticks[i] = (start - i) / step; + } + + if (reverse) ticks.reverse(); + + return ticks; +} + +function tickIncrement(start, stop, count) { + var step = (stop - start) / Math.max(0, count), + power = Math.floor(Math.log(step) / Math.LN10), + error = step / Math.pow(10, power); + return power >= 0 + ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power) + : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1); +} + +function tickStep(start, stop, count) { + var step0 = Math.abs(stop - start) / Math.max(0, count), + step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)), + error = step0 / step1; + if (error >= e10) step1 *= 10; + else if (error >= e5) step1 *= 5; + else if (error >= e2) step1 *= 2; + return stop < start ? -step1 : step1; +} + +function sturges(values) { + return Math.ceil(Math.log(values.length) / Math.LN2) + 1; +} + +function histogram() { + var value = identity, + domain = extent, + threshold = sturges; + + function histogram(data) { + var i, + n = data.length, + x, + values = new Array(n); + + for (i = 0; i < n; ++i) { + values[i] = value(data[i], i, data); + } + + var xz = domain(values), + x0 = xz[0], + x1 = xz[1], + tz = threshold(values, x0, x1); + + // Convert number of thresholds into uniform thresholds. + if (!Array.isArray(tz)) { + tz = tickStep(x0, x1, tz); + tz = sequence(Math.ceil(x0 / tz) * tz, Math.floor(x1 / tz) * tz, tz); // exclusive + } + + // Remove any thresholds outside the domain. + var m = tz.length; + while (tz[0] <= x0) tz.shift(), --m; + while (tz[m - 1] > x1) tz.pop(), --m; + + var bins = new Array(m + 1), + bin; + + // Initialize bins. + for (i = 0; i <= m; ++i) { + bin = bins[i] = []; + bin.x0 = i > 0 ? tz[i - 1] : x0; + bin.x1 = i < m ? tz[i] : x1; + } + + // Assign data to bins by value, ignoring any outside the domain. + for (i = 0; i < n; ++i) { + x = values[i]; + if (x0 <= x && x <= x1) { + bins[bisectRight(tz, x, 0, m)].push(data[i]); + } + } + + return bins; + } + + histogram.value = function(_) { + return arguments.length ? (value = typeof _ === "function" ? _ : constant(_), histogram) : value; + }; + + histogram.domain = function(_) { + return arguments.length ? (domain = typeof _ === "function" ? _ : constant([_[0], _[1]]), histogram) : domain; + }; + + histogram.thresholds = function(_) { + return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), histogram) : threshold; + }; + + return histogram; +} + +function threshold(values, p, valueof) { + if (valueof == null) valueof = number; + if (!(n = values.length)) return; + if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values); + if (p >= 1) return +valueof(values[n - 1], n - 1, values); + var n, + i = (n - 1) * p, + i0 = Math.floor(i), + value0 = +valueof(values[i0], i0, values), + value1 = +valueof(values[i0 + 1], i0 + 1, values); + return value0 + (value1 - value0) * (i - i0); +} + +function freedmanDiaconis(values, min, max) { + values = map.call(values, number).sort(ascending); + return Math.ceil((max - min) / (2 * (threshold(values, 0.75) - threshold(values, 0.25)) * Math.pow(values.length, -1 / 3))); +} + +function scott(values, min, max) { + return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3))); +} + +function max(values, valueof) { + var n = values.length, + i = -1, + value, + max; + + if (valueof == null) { + while (++i < n) { // Find the first comparable value. + if ((value = values[i]) != null && value >= value) { + max = value; + while (++i < n) { // Compare the remaining values. + if ((value = values[i]) != null && value > max) { + max = value; + } + } + } + } + } + + else { + while (++i < n) { // Find the first comparable value. + if ((value = valueof(values[i], i, values)) != null && value >= value) { + max = value; + while (++i < n) { // Compare the remaining values. + if ((value = valueof(values[i], i, values)) != null && value > max) { + max = value; + } + } + } + } + } + + return max; +} + +function mean(values, valueof) { + var n = values.length, + m = n, + i = -1, + value, + sum = 0; + + if (valueof == null) { + while (++i < n) { + if (!isNaN(value = number(values[i]))) sum += value; + else --m; + } + } + + else { + while (++i < n) { + if (!isNaN(value = number(valueof(values[i], i, values)))) sum += value; + else --m; + } + } + + if (m) return sum / m; +} + +function median(values, valueof) { + var n = values.length, + i = -1, + value, + numbers = []; + + if (valueof == null) { + while (++i < n) { + if (!isNaN(value = number(values[i]))) { + numbers.push(value); + } + } + } + + else { + while (++i < n) { + if (!isNaN(value = number(valueof(values[i], i, values)))) { + numbers.push(value); + } + } + } + + return threshold(numbers.sort(ascending), 0.5); +} + +function merge(arrays) { + var n = arrays.length, + m, + i = -1, + j = 0, + merged, + array; + + while (++i < n) j += arrays[i].length; + merged = new Array(j); + + while (--n >= 0) { + array = arrays[n]; + m = array.length; + while (--m >= 0) { + merged[--j] = array[m]; + } + } + + return merged; +} + +function min(values, valueof) { + var n = values.length, + i = -1, + value, + min; + + if (valueof == null) { + while (++i < n) { // Find the first comparable value. + if ((value = values[i]) != null && value >= value) { + min = value; + while (++i < n) { // Compare the remaining values. + if ((value = values[i]) != null && min > value) { + min = value; + } + } + } + } + } + + else { + while (++i < n) { // Find the first comparable value. + if ((value = valueof(values[i], i, values)) != null && value >= value) { + min = value; + while (++i < n) { // Compare the remaining values. + if ((value = valueof(values[i], i, values)) != null && min > value) { + min = value; + } + } + } + } + } + + return min; +} + +function permute(array, indexes) { + var i = indexes.length, permutes = new Array(i); + while (i--) permutes[i] = array[indexes[i]]; + return permutes; +} + +function scan(values, compare) { + if (!(n = values.length)) return; + var n, + i = 0, + j = 0, + xi, + xj = values[j]; + + if (compare == null) compare = ascending; + + while (++i < n) { + if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) { + xj = xi, j = i; + } + } + + if (compare(xj, xj) === 0) return j; +} + +function shuffle(array, i0, i1) { + var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0), + t, + i; + + while (m) { + i = Math.random() * m-- | 0; + t = array[m + i0]; + array[m + i0] = array[i + i0]; + array[i + i0] = t; + } + + return array; +} + +function sum(values, valueof) { + var n = values.length, + i = -1, + value, + sum = 0; + + if (valueof == null) { + while (++i < n) { + if (value = +values[i]) sum += value; // Note: zero and null are equivalent. + } + } + + else { + while (++i < n) { + if (value = +valueof(values[i], i, values)) sum += value; + } + } + + return sum; +} + +function transpose(matrix) { + if (!(n = matrix.length)) return []; + for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) { + for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) { + row[j] = matrix[j][i]; + } + } + return transpose; +} + +function length(d) { + return d.length; +} + +function zip() { + return transpose(arguments); +} + +var slice$1 = Array.prototype.slice; + +function identity$1(x) { + return x; +} + +var top = 1; +var right = 2; +var bottom = 3; +var left = 4; +var epsilon = 1e-6; + +function translateX(x) { + return "translate(" + (x + 0.5) + ",0)"; +} + +function translateY(y) { + return "translate(0," + (y + 0.5) + ")"; +} + +function number$1(scale) { + return function(d) { + return +scale(d); + }; +} + +function center(scale) { + var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset. + if (scale.round()) offset = Math.round(offset); + return function(d) { + return +scale(d) + offset; + }; +} + +function entering() { + return !this.__axis; +} + +function axis(orient, scale) { + var tickArguments = [], + tickValues = null, + tickFormat = null, + tickSizeInner = 6, + tickSizeOuter = 6, + tickPadding = 3, + k = orient === top || orient === left ? -1 : 1, + x = orient === left || orient === right ? "x" : "y", + transform = orient === top || orient === bottom ? translateX : translateY; + + function axis(context) { + var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues, + format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity$1) : tickFormat, + spacing = Math.max(tickSizeInner, 0) + tickPadding, + range = scale.range(), + range0 = +range[0] + 0.5, + range1 = +range[range.length - 1] + 0.5, + position = (scale.bandwidth ? center : number$1)(scale.copy()), + selection = context.selection ? context.selection() : context, + path = selection.selectAll(".domain").data([null]), + tick = selection.selectAll(".tick").data(values, scale).order(), + tickExit = tick.exit(), + tickEnter = tick.enter().append("g").attr("class", "tick"), + line = tick.select("line"), + text = tick.select("text"); + + path = path.merge(path.enter().insert("path", ".tick") + .attr("class", "domain") + .attr("stroke", "#000")); + + tick = tick.merge(tickEnter); + + line = line.merge(tickEnter.append("line") + .attr("stroke", "#000") + .attr(x + "2", k * tickSizeInner)); + + text = text.merge(tickEnter.append("text") + .attr("fill", "#000") + .attr(x, k * spacing) + .attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em")); + + if (context !== selection) { + path = path.transition(context); + tick = tick.transition(context); + line = line.transition(context); + text = text.transition(context); + + tickExit = tickExit.transition(context) + .attr("opacity", epsilon) + .attr("transform", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute("transform"); }); + + tickEnter + .attr("opacity", epsilon) + .attr("transform", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); }); + } + + tickExit.remove(); + + path + .attr("d", orient === left || orient == right + ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter + : "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter); + + tick + .attr("opacity", 1) + .attr("transform", function(d) { return transform(position(d)); }); + + line + .attr(x + "2", k * tickSizeInner); + + text + .attr(x, k * spacing) + .text(format); + + selection.filter(entering) + .attr("fill", "none") + .attr("font-size", 10) + .attr("font-family", "sans-serif") + .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle"); + + selection + .each(function() { this.__axis = position; }); + } + + axis.scale = function(_) { + return arguments.length ? (scale = _, axis) : scale; + }; + + axis.ticks = function() { + return tickArguments = slice$1.call(arguments), axis; + }; + + axis.tickArguments = function(_) { + return arguments.length ? (tickArguments = _ == null ? [] : slice$1.call(_), axis) : tickArguments.slice(); + }; + + axis.tickValues = function(_) { + return arguments.length ? (tickValues = _ == null ? null : slice$1.call(_), axis) : tickValues && tickValues.slice(); + }; + + axis.tickFormat = function(_) { + return arguments.length ? (tickFormat = _, axis) : tickFormat; + }; + + axis.tickSize = function(_) { + return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner; + }; + + axis.tickSizeInner = function(_) { + return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner; + }; + + axis.tickSizeOuter = function(_) { + return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter; + }; + + axis.tickPadding = function(_) { + return arguments.length ? (tickPadding = +_, axis) : tickPadding; + }; + + return axis; +} + +function axisTop(scale) { + return axis(top, scale); +} + +function axisRight(scale) { + return axis(right, scale); +} + +function axisBottom(scale) { + return axis(bottom, scale); +} + +function axisLeft(scale) { + return axis(left, scale); +} + +var noop = {value: function() {}}; + +function dispatch() { + for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { + if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t); + _[t] = []; + } + return new Dispatch(_); +} + +function Dispatch(_) { + this._ = _; +} + +function parseTypenames(typenames, types) { + return typenames.trim().split(/^|\s+/).map(function(t) { + var name = "", i = t.indexOf("."); + if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); + if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); + return {type: t, name: name}; + }); +} + +Dispatch.prototype = dispatch.prototype = { + constructor: Dispatch, + on: function(typename, callback) { + var _ = this._, + T = parseTypenames(typename + "", _), + t, + i = -1, + n = T.length; + + // If no callback was specified, return the callback of the given type and name. + if (arguments.length < 2) { + while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t; + return; + } + + // If a type was specified, set the callback for the given type and name. + // Otherwise, if a null callback was specified, remove callbacks of the given name. + if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); + while (++i < n) { + if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback); + else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null); + } + + return this; + }, + copy: function() { + var copy = {}, _ = this._; + for (var t in _) copy[t] = _[t].slice(); + return new Dispatch(copy); + }, + call: function(type, that) { + if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; + if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); + for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); + }, + apply: function(type, that, args) { + if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); + for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); + } +}; + +function get(type, name) { + for (var i = 0, n = type.length, c; i < n; ++i) { + if ((c = type[i]).name === name) { + return c.value; + } + } +} + +function set(type, name, callback) { + for (var i = 0, n = type.length; i < n; ++i) { + if (type[i].name === name) { + type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1)); + break; + } + } + if (callback != null) type.push({name: name, value: callback}); + return type; +} + +var xhtml = "http://www.w3.org/1999/xhtml"; + +var namespaces = { + svg: "http://www.w3.org/2000/svg", + xhtml: xhtml, + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" +}; + +function namespace(name) { + var prefix = name += "", i = prefix.indexOf(":"); + if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); + return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name; +} + +function creatorInherit(name) { + return function() { + var document = this.ownerDocument, + uri = this.namespaceURI; + return uri === xhtml && document.documentElement.namespaceURI === xhtml + ? document.createElement(name) + : document.createElementNS(uri, name); + }; +} + +function creatorFixed(fullname) { + return function() { + return this.ownerDocument.createElementNS(fullname.space, fullname.local); + }; +} + +function creator(name) { + var fullname = namespace(name); + return (fullname.local + ? creatorFixed + : creatorInherit)(fullname); +} + +var nextId = 0; + +function local$1() { + return new Local; +} + +function Local() { + this._ = "@" + (++nextId).toString(36); +} + +Local.prototype = local$1.prototype = { + constructor: Local, + get: function(node) { + var id = this._; + while (!(id in node)) if (!(node = node.parentNode)) return; + return node[id]; + }, + set: function(node, value) { + return node[this._] = value; + }, + remove: function(node) { + return this._ in node && delete node[this._]; + }, + toString: function() { + return this._; + } +}; + +var matcher = function(selector) { + return function() { + return this.matches(selector); + }; +}; + +if (typeof document !== "undefined") { + var element = document.documentElement; + if (!element.matches) { + var vendorMatches = element.webkitMatchesSelector + || element.msMatchesSelector + || element.mozMatchesSelector + || element.oMatchesSelector; + matcher = function(selector) { + return function() { + return vendorMatches.call(this, selector); + }; + }; + } +} + +var matcher$1 = matcher; + +var filterEvents = {}; + +exports.event = null; + +if (typeof document !== "undefined") { + var element$1 = document.documentElement; + if (!("onmouseenter" in element$1)) { + filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"}; + } +} + +function filterContextListener(listener, index, group) { + listener = contextListener(listener, index, group); + return function(event) { + var related = event.relatedTarget; + if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) { + listener.call(this, event); + } + }; +} + +function contextListener(listener, index, group) { + return function(event1) { + var event0 = exports.event; // Events can be reentrant (e.g., focus). + exports.event = event1; + try { + listener.call(this, this.__data__, index, group); + } finally { + exports.event = event0; + } + }; +} + +function parseTypenames$1(typenames) { + return typenames.trim().split(/^|\s+/).map(function(t) { + var name = "", i = t.indexOf("."); + if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); + return {type: t, name: name}; + }); +} + +function onRemove(typename) { + return function() { + var on = this.__on; + if (!on) return; + for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { + if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { + this.removeEventListener(o.type, o.listener, o.capture); + } else { + on[++i] = o; + } + } + if (++i) on.length = i; + else delete this.__on; + }; +} + +function onAdd(typename, value, capture) { + var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener; + return function(d, i, group) { + var on = this.__on, o, listener = wrap(value, i, group); + if (on) for (var j = 0, m = on.length; j < m; ++j) { + if ((o = on[j]).type === typename.type && o.name === typename.name) { + this.removeEventListener(o.type, o.listener, o.capture); + this.addEventListener(o.type, o.listener = listener, o.capture = capture); + o.value = value; + return; + } + } + this.addEventListener(typename.type, listener, capture); + o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture}; + if (!on) this.__on = [o]; + else on.push(o); + }; +} + +function selection_on(typename, value, capture) { + var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t; + + if (arguments.length < 2) { + var on = this.node().__on; + if (on) for (var j = 0, m = on.length, o; j < m; ++j) { + for (i = 0, o = on[j]; i < n; ++i) { + if ((t = typenames[i]).type === o.type && t.name === o.name) { + return o.value; + } + } + } + return; + } + + on = value ? onAdd : onRemove; + if (capture == null) capture = false; + for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture)); + return this; +} + +function customEvent(event1, listener, that, args) { + var event0 = exports.event; + event1.sourceEvent = exports.event; + exports.event = event1; + try { + return listener.apply(that, args); + } finally { + exports.event = event0; + } +} + +function sourceEvent() { + var current = exports.event, source; + while (source = current.sourceEvent) current = source; + return current; +} + +function point(node, event) { + var svg = node.ownerSVGElement || node; + + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + point.x = event.clientX, point.y = event.clientY; + point = point.matrixTransform(node.getScreenCTM().inverse()); + return [point.x, point.y]; + } + + var rect = node.getBoundingClientRect(); + return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop]; +} + +function mouse(node) { + var event = sourceEvent(); + if (event.changedTouches) event = event.changedTouches[0]; + return point(node, event); +} + +function none() {} + +function selector(selector) { + return selector == null ? none : function() { + return this.querySelector(selector); + }; +} + +function selection_select(select) { + if (typeof select !== "function") select = selector(select); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { + if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + subgroup[i] = subnode; + } + } + } + + return new Selection(subgroups, this._parents); +} + +function empty$1() { + return []; +} + +function selectorAll(selector) { + return selector == null ? empty$1 : function() { + return this.querySelectorAll(selector); + }; +} + +function selection_selectAll(select) { + if (typeof select !== "function") select = selectorAll(select); + + for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + subgroups.push(select.call(node, node.__data__, i, group)); + parents.push(node); + } + } + } + + return new Selection(subgroups, parents); +} + +function selection_filter(match) { + if (typeof match !== "function") match = matcher$1(match); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { + if ((node = group[i]) && match.call(node, node.__data__, i, group)) { + subgroup.push(node); + } + } + } + + return new Selection(subgroups, this._parents); +} + +function sparse(update) { + return new Array(update.length); +} + +function selection_enter() { + return new Selection(this._enter || this._groups.map(sparse), this._parents); +} + +function EnterNode(parent, datum) { + this.ownerDocument = parent.ownerDocument; + this.namespaceURI = parent.namespaceURI; + this._next = null; + this._parent = parent; + this.__data__ = datum; +} + +EnterNode.prototype = { + constructor: EnterNode, + appendChild: function(child) { return this._parent.insertBefore(child, this._next); }, + insertBefore: function(child, next) { return this._parent.insertBefore(child, next); }, + querySelector: function(selector) { return this._parent.querySelector(selector); }, + querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); } +}; + +function constant$1(x) { + return function() { + return x; + }; +} + +var keyPrefix = "$"; // Protect against keys like “__proto__”. + +function bindIndex(parent, group, enter, update, exit, data) { + var i = 0, + node, + groupLength = group.length, + dataLength = data.length; + + // Put any non-null nodes that fit into update. + // Put any null nodes into enter. + // Put any remaining data into enter. + for (; i < dataLength; ++i) { + if (node = group[i]) { + node.__data__ = data[i]; + update[i] = node; + } else { + enter[i] = new EnterNode(parent, data[i]); + } + } + + // Put any non-null nodes that don’t fit into exit. + for (; i < groupLength; ++i) { + if (node = group[i]) { + exit[i] = node; + } + } +} + +function bindKey(parent, group, enter, update, exit, data, key) { + var i, + node, + nodeByKeyValue = {}, + groupLength = group.length, + dataLength = data.length, + keyValues = new Array(groupLength), + keyValue; + + // Compute the key for each node. + // If multiple nodes have the same key, the duplicates are added to exit. + for (i = 0; i < groupLength; ++i) { + if (node = group[i]) { + keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group); + if (keyValue in nodeByKeyValue) { + exit[i] = node; + } else { + nodeByKeyValue[keyValue] = node; + } + } + } + + // Compute the key for each datum. + // If there a node associated with this key, join and add it to update. + // If there is not (or the key is a duplicate), add it to enter. + for (i = 0; i < dataLength; ++i) { + keyValue = keyPrefix + key.call(parent, data[i], i, data); + if (node = nodeByKeyValue[keyValue]) { + update[i] = node; + node.__data__ = data[i]; + nodeByKeyValue[keyValue] = null; + } else { + enter[i] = new EnterNode(parent, data[i]); + } + } + + // Add any remaining nodes that were not bound to data to exit. + for (i = 0; i < groupLength; ++i) { + if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) { + exit[i] = node; + } + } +} + +function selection_data(value, key) { + if (!value) { + data = new Array(this.size()), j = -1; + this.each(function(d) { data[++j] = d; }); + return data; + } + + var bind = key ? bindKey : bindIndex, + parents = this._parents, + groups = this._groups; + + if (typeof value !== "function") value = constant$1(value); + + for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { + var parent = parents[j], + group = groups[j], + groupLength = group.length, + data = value.call(parent, parent && parent.__data__, j, parents), + dataLength = data.length, + enterGroup = enter[j] = new Array(dataLength), + updateGroup = update[j] = new Array(dataLength), + exitGroup = exit[j] = new Array(groupLength); + + bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); + + // Now connect the enter nodes to their following update node, such that + // appendChild can insert the materialized enter node before this node, + // rather than at the end of the parent node. + for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { + if (previous = enterGroup[i0]) { + if (i0 >= i1) i1 = i0 + 1; + while (!(next = updateGroup[i1]) && ++i1 < dataLength); + previous._next = next || null; + } + } + } + + update = new Selection(update, parents); + update._enter = enter; + update._exit = exit; + return update; +} + +function selection_exit() { + return new Selection(this._exit || this._groups.map(sparse), this._parents); +} + +function selection_merge(selection$$1) { + + for (var groups0 = this._groups, groups1 = selection$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { + for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group0[i] || group1[i]) { + merge[i] = node; + } + } + } + + for (; j < m0; ++j) { + merges[j] = groups0[j]; + } + + return new Selection(merges, this._parents); +} + +function selection_order() { + + for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) { + for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + + return this; +} + +function selection_sort(compare) { + if (!compare) compare = ascending$1; + + function compareNode(a, b) { + return a && b ? compare(a.__data__, b.__data__) : !a - !b; + } + + for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group[i]) { + sortgroup[i] = node; + } + } + sortgroup.sort(compareNode); + } + + return new Selection(sortgroups, this._parents).order(); +} + +function ascending$1(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; +} + +function selection_call() { + var callback = arguments[0]; + arguments[0] = this; + callback.apply(null, arguments); + return this; +} + +function selection_nodes() { + var nodes = new Array(this.size()), i = -1; + this.each(function() { nodes[++i] = this; }); + return nodes; +} + +function selection_node() { + + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { + var node = group[i]; + if (node) return node; + } + } + + return null; +} + +function selection_size() { + var size = 0; + this.each(function() { ++size; }); + return size; +} + +function selection_empty() { + return !this.node(); +} + +function selection_each(callback) { + + for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { + if (node = group[i]) callback.call(node, node.__data__, i, group); + } + } + + return this; +} + +function attrRemove(name) { + return function() { + this.removeAttribute(name); + }; +} + +function attrRemoveNS(fullname) { + return function() { + this.removeAttributeNS(fullname.space, fullname.local); + }; +} + +function attrConstant(name, value) { + return function() { + this.setAttribute(name, value); + }; +} + +function attrConstantNS(fullname, value) { + return function() { + this.setAttributeNS(fullname.space, fullname.local, value); + }; +} + +function attrFunction(name, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.removeAttribute(name); + else this.setAttribute(name, v); + }; +} + +function attrFunctionNS(fullname, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.removeAttributeNS(fullname.space, fullname.local); + else this.setAttributeNS(fullname.space, fullname.local, v); + }; +} + +function selection_attr(name, value) { + var fullname = namespace(name); + + if (arguments.length < 2) { + var node = this.node(); + return fullname.local + ? node.getAttributeNS(fullname.space, fullname.local) + : node.getAttribute(fullname); + } + + return this.each((value == null + ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function" + ? (fullname.local ? attrFunctionNS : attrFunction) + : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value)); +} + +function defaultView(node) { + return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node + || (node.document && node) // node is a Window + || node.defaultView; // node is a Document +} + +function styleRemove(name) { + return function() { + this.style.removeProperty(name); + }; +} + +function styleConstant(name, value, priority) { + return function() { + this.style.setProperty(name, value, priority); + }; +} + +function styleFunction(name, value, priority) { + return function() { + var v = value.apply(this, arguments); + if (v == null) this.style.removeProperty(name); + else this.style.setProperty(name, v, priority); + }; +} + +function selection_style(name, value, priority) { + return arguments.length > 1 + ? this.each((value == null + ? styleRemove : typeof value === "function" + ? styleFunction + : styleConstant)(name, value, priority == null ? "" : priority)) + : styleValue(this.node(), name); +} + +function styleValue(node, name) { + return node.style.getPropertyValue(name) + || defaultView(node).getComputedStyle(node, null).getPropertyValue(name); +} + +function propertyRemove(name) { + return function() { + delete this[name]; + }; +} + +function propertyConstant(name, value) { + return function() { + this[name] = value; + }; +} + +function propertyFunction(name, value) { + return function() { + var v = value.apply(this, arguments); + if (v == null) delete this[name]; + else this[name] = v; + }; +} + +function selection_property(name, value) { + return arguments.length > 1 + ? this.each((value == null + ? propertyRemove : typeof value === "function" + ? propertyFunction + : propertyConstant)(name, value)) + : this.node()[name]; +} + +function classArray(string) { + return string.trim().split(/^|\s+/); +} + +function classList(node) { + return node.classList || new ClassList(node); +} + +function ClassList(node) { + this._node = node; + this._names = classArray(node.getAttribute("class") || ""); +} + +ClassList.prototype = { + add: function(name) { + var i = this._names.indexOf(name); + if (i < 0) { + this._names.push(name); + this._node.setAttribute("class", this._names.join(" ")); + } + }, + remove: function(name) { + var i = this._names.indexOf(name); + if (i >= 0) { + this._names.splice(i, 1); + this._node.setAttribute("class", this._names.join(" ")); + } + }, + contains: function(name) { + return this._names.indexOf(name) >= 0; + } +}; + +function classedAdd(node, names) { + var list = classList(node), i = -1, n = names.length; + while (++i < n) list.add(names[i]); +} + +function classedRemove(node, names) { + var list = classList(node), i = -1, n = names.length; + while (++i < n) list.remove(names[i]); +} + +function classedTrue(names) { + return function() { + classedAdd(this, names); + }; +} + +function classedFalse(names) { + return function() { + classedRemove(this, names); + }; +} + +function classedFunction(names, value) { + return function() { + (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); + }; +} + +function selection_classed(name, value) { + var names = classArray(name + ""); + + if (arguments.length < 2) { + var list = classList(this.node()), i = -1, n = names.length; + while (++i < n) if (!list.contains(names[i])) return false; + return true; + } + + return this.each((typeof value === "function" + ? classedFunction : value + ? classedTrue + : classedFalse)(names, value)); +} + +function textRemove() { + this.textContent = ""; +} + +function textConstant(value) { + return function() { + this.textContent = value; + }; +} + +function textFunction(value) { + return function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + }; +} + +function selection_text(value) { + return arguments.length + ? this.each(value == null + ? textRemove : (typeof value === "function" + ? textFunction + : textConstant)(value)) + : this.node().textContent; +} + +function htmlRemove() { + this.innerHTML = ""; +} + +function htmlConstant(value) { + return function() { + this.innerHTML = value; + }; +} + +function htmlFunction(value) { + return function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + }; +} + +function selection_html(value) { + return arguments.length + ? this.each(value == null + ? htmlRemove : (typeof value === "function" + ? htmlFunction + : htmlConstant)(value)) + : this.node().innerHTML; +} + +function raise() { + if (this.nextSibling) this.parentNode.appendChild(this); +} + +function selection_raise() { + return this.each(raise); +} + +function lower() { + if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); +} + +function selection_lower() { + return this.each(lower); +} + +function selection_append(name) { + var create = typeof name === "function" ? name : creator(name); + return this.select(function() { + return this.appendChild(create.apply(this, arguments)); + }); +} + +function constantNull() { + return null; +} + +function selection_insert(name, before) { + var create = typeof name === "function" ? name : creator(name), + select = before == null ? constantNull : typeof before === "function" ? before : selector(before); + return this.select(function() { + return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); + }); +} + +function remove() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); +} + +function selection_remove() { + return this.each(remove); +} + +function selection_datum(value) { + return arguments.length + ? this.property("__data__", value) + : this.node().__data__; +} + +function dispatchEvent(node, type, params) { + var window = defaultView(node), + event = window.CustomEvent; + + if (typeof event === "function") { + event = new event(type, params); + } else { + event = window.document.createEvent("Event"); + if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; + else event.initEvent(type, false, false); + } + + node.dispatchEvent(event); +} + +function dispatchConstant(type, params) { + return function() { + return dispatchEvent(this, type, params); + }; +} + +function dispatchFunction(type, params) { + return function() { + return dispatchEvent(this, type, params.apply(this, arguments)); + }; +} + +function selection_dispatch(type, params) { + return this.each((typeof params === "function" + ? dispatchFunction + : dispatchConstant)(type, params)); +} + +var root = [null]; + +function Selection(groups, parents) { + this._groups = groups; + this._parents = parents; +} + +function selection() { + return new Selection([[document.documentElement]], root); +} + +Selection.prototype = selection.prototype = { + constructor: Selection, + select: selection_select, + selectAll: selection_selectAll, + filter: selection_filter, + data: selection_data, + enter: selection_enter, + exit: selection_exit, + merge: selection_merge, + order: selection_order, + sort: selection_sort, + call: selection_call, + nodes: selection_nodes, + node: selection_node, + size: selection_size, + empty: selection_empty, + each: selection_each, + attr: selection_attr, + style: selection_style, + property: selection_property, + classed: selection_classed, + text: selection_text, + html: selection_html, + raise: selection_raise, + lower: selection_lower, + append: selection_append, + insert: selection_insert, + remove: selection_remove, + datum: selection_datum, + on: selection_on, + dispatch: selection_dispatch +}; + +function select(selector) { + return typeof selector === "string" + ? new Selection([[document.querySelector(selector)]], [document.documentElement]) + : new Selection([[selector]], root); +} + +function selectAll(selector) { + return typeof selector === "string" + ? new Selection([document.querySelectorAll(selector)], [document.documentElement]) + : new Selection([selector == null ? [] : selector], root); +} + +function touch(node, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches; + + for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return point(node, touch); + } + } + + return null; +} + +function touches(node, touches) { + if (touches == null) touches = sourceEvent().touches; + + for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) { + points[i] = point(node, touches[i]); + } + + return points; +} + +function nopropagation() { + exports.event.stopImmediatePropagation(); +} + +function noevent() { + exports.event.preventDefault(); + exports.event.stopImmediatePropagation(); +} + +function dragDisable(view) { + var root = view.document.documentElement, + selection = select(view).on("dragstart.drag", noevent, true); + if ("onselectstart" in root) { + selection.on("selectstart.drag", noevent, true); + } else { + root.__noselect = root.style.MozUserSelect; + root.style.MozUserSelect = "none"; + } +} + +function yesdrag(view, noclick) { + var root = view.document.documentElement, + selection = select(view).on("dragstart.drag", null); + if (noclick) { + selection.on("click.drag", noevent, true); + setTimeout(function() { selection.on("click.drag", null); }, 0); + } + if ("onselectstart" in root) { + selection.on("selectstart.drag", null); + } else { + root.style.MozUserSelect = root.__noselect; + delete root.__noselect; + } +} + +function constant$2(x) { + return function() { + return x; + }; +} + +function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) { + this.target = target; + this.type = type; + this.subject = subject; + this.identifier = id; + this.active = active; + this.x = x; + this.y = y; + this.dx = dx; + this.dy = dy; + this._ = dispatch; +} + +DragEvent.prototype.on = function() { + var value = this._.on.apply(this._, arguments); + return value === this._ ? this : value; +}; + +// Ignore right-click, since that should open the context menu. +function defaultFilter$1() { + return !exports.event.button; +} + +function defaultContainer() { + return this.parentNode; +} + +function defaultSubject(d) { + return d == null ? {x: exports.event.x, y: exports.event.y} : d; +} + +function defaultTouchable() { + return "ontouchstart" in this; +} + +function drag() { + var filter = defaultFilter$1, + container = defaultContainer, + subject = defaultSubject, + touchable = defaultTouchable, + gestures = {}, + listeners = dispatch("start", "drag", "end"), + active = 0, + mousedownx, + mousedowny, + mousemoving, + touchending, + clickDistance2 = 0; + + function drag(selection) { + selection + .on("mousedown.drag", mousedowned) + .filter(touchable) + .on("touchstart.drag", touchstarted) + .on("touchmove.drag", touchmoved) + .on("touchend.drag touchcancel.drag", touchended) + .style("touch-action", "none") + .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); + } + + function mousedowned() { + if (touchending || !filter.apply(this, arguments)) return; + var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments); + if (!gesture) return; + select(exports.event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true); + dragDisable(exports.event.view); + nopropagation(); + mousemoving = false; + mousedownx = exports.event.clientX; + mousedowny = exports.event.clientY; + gesture("start"); + } + + function mousemoved() { + noevent(); + if (!mousemoving) { + var dx = exports.event.clientX - mousedownx, dy = exports.event.clientY - mousedowny; + mousemoving = dx * dx + dy * dy > clickDistance2; + } + gestures.mouse("drag"); + } + + function mouseupped() { + select(exports.event.view).on("mousemove.drag mouseup.drag", null); + yesdrag(exports.event.view, mousemoving); + noevent(); + gestures.mouse("end"); + } + + function touchstarted() { + if (!filter.apply(this, arguments)) return; + var touches = exports.event.changedTouches, + c = container.apply(this, arguments), + n = touches.length, i, gesture; + + for (i = 0; i < n; ++i) { + if (gesture = beforestart(touches[i].identifier, c, touch, this, arguments)) { + nopropagation(); + gesture("start"); + } + } + } + + function touchmoved() { + var touches = exports.event.changedTouches, + n = touches.length, i, gesture; + + for (i = 0; i < n; ++i) { + if (gesture = gestures[touches[i].identifier]) { + noevent(); + gesture("drag"); + } + } + } + + function touchended() { + var touches = exports.event.changedTouches, + n = touches.length, i, gesture; + + if (touchending) clearTimeout(touchending); + touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! + for (i = 0; i < n; ++i) { + if (gesture = gestures[touches[i].identifier]) { + nopropagation(); + gesture("end"); + } + } + } + + function beforestart(id, container, point, that, args) { + var p = point(container, id), s, dx, dy, + sublisteners = listeners.copy(); + + if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() { + if ((exports.event.subject = s = subject.apply(that, args)) == null) return false; + dx = s.x - p[0] || 0; + dy = s.y - p[1] || 0; + return true; + })) return; + + return function gesture(type) { + var p0 = p, n; + switch (type) { + case "start": gestures[id] = gesture, n = active++; break; + case "end": delete gestures[id], --active; // nobreak + case "drag": p = point(container, id), n = active; break; + } + customEvent(new DragEvent(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]); + }; + } + + drag.filter = function(_) { + return arguments.length ? (filter = typeof _ === "function" ? _ : constant$2(!!_), drag) : filter; + }; + + drag.container = function(_) { + return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag) : container; + }; + + drag.subject = function(_) { + return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag) : subject; + }; + + drag.touchable = function(_) { + return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag) : touchable; + }; + + drag.on = function() { + var value = listeners.on.apply(listeners, arguments); + return value === listeners ? drag : value; + }; + + drag.clickDistance = function(_) { + return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2); + }; + + return drag; +} + +function define(constructor, factory, prototype) { + constructor.prototype = factory.prototype = prototype; + prototype.constructor = constructor; +} + +function extend(parent, definition) { + var prototype = Object.create(parent.prototype); + for (var key in definition) prototype[key] = definition[key]; + return prototype; +} + +function Color() {} + +var darker = 0.7; +var brighter = 1 / darker; + +var reI = "\\s*([+-]?\\d+)\\s*"; +var reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*"; +var reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*"; +var reHex3 = /^#([0-9a-f]{3})$/; +var reHex6 = /^#([0-9a-f]{6})$/; +var reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"); +var reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"); +var reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"); +var reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"); +var reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"); +var reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$"); + +var named = { + aliceblue: 0xf0f8ff, + antiquewhite: 0xfaebd7, + aqua: 0x00ffff, + aquamarine: 0x7fffd4, + azure: 0xf0ffff, + beige: 0xf5f5dc, + bisque: 0xffe4c4, + black: 0x000000, + blanchedalmond: 0xffebcd, + blue: 0x0000ff, + blueviolet: 0x8a2be2, + brown: 0xa52a2a, + burlywood: 0xdeb887, + cadetblue: 0x5f9ea0, + chartreuse: 0x7fff00, + chocolate: 0xd2691e, + coral: 0xff7f50, + cornflowerblue: 0x6495ed, + cornsilk: 0xfff8dc, + crimson: 0xdc143c, + cyan: 0x00ffff, + darkblue: 0x00008b, + darkcyan: 0x008b8b, + darkgoldenrod: 0xb8860b, + darkgray: 0xa9a9a9, + darkgreen: 0x006400, + darkgrey: 0xa9a9a9, + darkkhaki: 0xbdb76b, + darkmagenta: 0x8b008b, + darkolivegreen: 0x556b2f, + darkorange: 0xff8c00, + darkorchid: 0x9932cc, + darkred: 0x8b0000, + darksalmon: 0xe9967a, + darkseagreen: 0x8fbc8f, + darkslateblue: 0x483d8b, + darkslategray: 0x2f4f4f, + darkslategrey: 0x2f4f4f, + darkturquoise: 0x00ced1, + darkviolet: 0x9400d3, + deeppink: 0xff1493, + deepskyblue: 0x00bfff, + dimgray: 0x696969, + dimgrey: 0x696969, + dodgerblue: 0x1e90ff, + firebrick: 0xb22222, + floralwhite: 0xfffaf0, + forestgreen: 0x228b22, + fuchsia: 0xff00ff, + gainsboro: 0xdcdcdc, + ghostwhite: 0xf8f8ff, + gold: 0xffd700, + goldenrod: 0xdaa520, + gray: 0x808080, + green: 0x008000, + greenyellow: 0xadff2f, + grey: 0x808080, + honeydew: 0xf0fff0, + hotpink: 0xff69b4, + indianred: 0xcd5c5c, + indigo: 0x4b0082, + ivory: 0xfffff0, + khaki: 0xf0e68c, + lavender: 0xe6e6fa, + lavenderblush: 0xfff0f5, + lawngreen: 0x7cfc00, + lemonchiffon: 0xfffacd, + lightblue: 0xadd8e6, + lightcoral: 0xf08080, + lightcyan: 0xe0ffff, + lightgoldenrodyellow: 0xfafad2, + lightgray: 0xd3d3d3, + lightgreen: 0x90ee90, + lightgrey: 0xd3d3d3, + lightpink: 0xffb6c1, + lightsalmon: 0xffa07a, + lightseagreen: 0x20b2aa, + lightskyblue: 0x87cefa, + lightslategray: 0x778899, + lightslategrey: 0x778899, + lightsteelblue: 0xb0c4de, + lightyellow: 0xffffe0, + lime: 0x00ff00, + limegreen: 0x32cd32, + linen: 0xfaf0e6, + magenta: 0xff00ff, + maroon: 0x800000, + mediumaquamarine: 0x66cdaa, + mediumblue: 0x0000cd, + mediumorchid: 0xba55d3, + mediumpurple: 0x9370db, + mediumseagreen: 0x3cb371, + mediumslateblue: 0x7b68ee, + mediumspringgreen: 0x00fa9a, + mediumturquoise: 0x48d1cc, + mediumvioletred: 0xc71585, + midnightblue: 0x191970, + mintcream: 0xf5fffa, + mistyrose: 0xffe4e1, + moccasin: 0xffe4b5, + navajowhite: 0xffdead, + navy: 0x000080, + oldlace: 0xfdf5e6, + olive: 0x808000, + olivedrab: 0x6b8e23, + orange: 0xffa500, + orangered: 0xff4500, + orchid: 0xda70d6, + palegoldenrod: 0xeee8aa, + palegreen: 0x98fb98, + paleturquoise: 0xafeeee, + palevioletred: 0xdb7093, + papayawhip: 0xffefd5, + peachpuff: 0xffdab9, + peru: 0xcd853f, + pink: 0xffc0cb, + plum: 0xdda0dd, + powderblue: 0xb0e0e6, + purple: 0x800080, + rebeccapurple: 0x663399, + red: 0xff0000, + rosybrown: 0xbc8f8f, + royalblue: 0x4169e1, + saddlebrown: 0x8b4513, + salmon: 0xfa8072, + sandybrown: 0xf4a460, + seagreen: 0x2e8b57, + seashell: 0xfff5ee, + sienna: 0xa0522d, + silver: 0xc0c0c0, + skyblue: 0x87ceeb, + slateblue: 0x6a5acd, + slategray: 0x708090, + slategrey: 0x708090, + snow: 0xfffafa, + springgreen: 0x00ff7f, + steelblue: 0x4682b4, + tan: 0xd2b48c, + teal: 0x008080, + thistle: 0xd8bfd8, + tomato: 0xff6347, + turquoise: 0x40e0d0, + violet: 0xee82ee, + wheat: 0xf5deb3, + white: 0xffffff, + whitesmoke: 0xf5f5f5, + yellow: 0xffff00, + yellowgreen: 0x9acd32 +}; + +define(Color, color, { + displayable: function() { + return this.rgb().displayable(); + }, + toString: function() { + return this.rgb() + ""; + } +}); + +function color(format) { + var m; + format = (format + "").trim().toLowerCase(); + return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00 + : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000 + : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) + : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) + : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) + : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) + : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) + : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) + : named.hasOwnProperty(format) ? rgbn(named[format]) + : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) + : null; +} + +function rgbn(n) { + return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1); +} + +function rgba(r, g, b, a) { + if (a <= 0) r = g = b = NaN; + return new Rgb(r, g, b, a); +} + +function rgbConvert(o) { + if (!(o instanceof Color)) o = color(o); + if (!o) return new Rgb; + o = o.rgb(); + return new Rgb(o.r, o.g, o.b, o.opacity); +} + +function rgb(r, g, b, opacity) { + return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity); +} + +function Rgb(r, g, b, opacity) { + this.r = +r; + this.g = +g; + this.b = +b; + this.opacity = +opacity; +} + +define(Rgb, rgb, extend(Color, { + brighter: function(k) { + k = k == null ? brighter : Math.pow(brighter, k); + return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); + }, + darker: function(k) { + k = k == null ? darker : Math.pow(darker, k); + return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); + }, + rgb: function() { + return this; + }, + displayable: function() { + return (0 <= this.r && this.r <= 255) + && (0 <= this.g && this.g <= 255) + && (0 <= this.b && this.b <= 255) + && (0 <= this.opacity && this.opacity <= 1); + }, + toString: function() { + var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a)); + return (a === 1 ? "rgb(" : "rgba(") + + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", " + + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", " + + Math.max(0, Math.min(255, Math.round(this.b) || 0)) + + (a === 1 ? ")" : ", " + a + ")"); + } +})); + +function hsla(h, s, l, a) { + if (a <= 0) h = s = l = NaN; + else if (l <= 0 || l >= 1) h = s = NaN; + else if (s <= 0) h = NaN; + return new Hsl(h, s, l, a); +} + +function hslConvert(o) { + if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity); + if (!(o instanceof Color)) o = color(o); + if (!o) return new Hsl; + if (o instanceof Hsl) return o; + o = o.rgb(); + var r = o.r / 255, + g = o.g / 255, + b = o.b / 255, + min = Math.min(r, g, b), + max = Math.max(r, g, b), + h = NaN, + s = max - min, + l = (max + min) / 2; + if (s) { + if (r === max) h = (g - b) / s + (g < b) * 6; + else if (g === max) h = (b - r) / s + 2; + else h = (r - g) / s + 4; + s /= l < 0.5 ? max + min : 2 - max - min; + h *= 60; + } else { + s = l > 0 && l < 1 ? 0 : h; + } + return new Hsl(h, s, l, o.opacity); +} + +function hsl(h, s, l, opacity) { + return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity); +} + +function Hsl(h, s, l, opacity) { + this.h = +h; + this.s = +s; + this.l = +l; + this.opacity = +opacity; +} + +define(Hsl, hsl, extend(Color, { + brighter: function(k) { + k = k == null ? brighter : Math.pow(brighter, k); + return new Hsl(this.h, this.s, this.l * k, this.opacity); + }, + darker: function(k) { + k = k == null ? darker : Math.pow(darker, k); + return new Hsl(this.h, this.s, this.l * k, this.opacity); + }, + rgb: function() { + var h = this.h % 360 + (this.h < 0) * 360, + s = isNaN(h) || isNaN(this.s) ? 0 : this.s, + l = this.l, + m2 = l + (l < 0.5 ? l : 1 - l) * s, + m1 = 2 * l - m2; + return new Rgb( + hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), + hsl2rgb(h, m1, m2), + hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), + this.opacity + ); + }, + displayable: function() { + return (0 <= this.s && this.s <= 1 || isNaN(this.s)) + && (0 <= this.l && this.l <= 1) + && (0 <= this.opacity && this.opacity <= 1); + } +})); + +/* From FvD 13.37, CSS Color Module Level 3 */ +function hsl2rgb(h, m1, m2) { + return (h < 60 ? m1 + (m2 - m1) * h / 60 + : h < 180 ? m2 + : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 + : m1) * 255; +} + +var deg2rad = Math.PI / 180; +var rad2deg = 180 / Math.PI; + +var Kn = 18; +var Xn = 0.950470; +var Yn = 1; +var Zn = 1.088830; +var t0 = 4 / 29; +var t1 = 6 / 29; +var t2 = 3 * t1 * t1; +var t3 = t1 * t1 * t1; + +function labConvert(o) { + if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity); + if (o instanceof Hcl) { + var h = o.h * deg2rad; + return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity); + } + if (!(o instanceof Rgb)) o = rgbConvert(o); + var b = rgb2xyz(o.r), + a = rgb2xyz(o.g), + l = rgb2xyz(o.b), + x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn), + y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.0721750 * l) / Yn), + z = xyz2lab((0.0193339 * b + 0.1191920 * a + 0.9503041 * l) / Zn); + return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity); +} + +function lab(l, a, b, opacity) { + return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity); +} + +function Lab(l, a, b, opacity) { + this.l = +l; + this.a = +a; + this.b = +b; + this.opacity = +opacity; +} + +define(Lab, lab, extend(Color, { + brighter: function(k) { + return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity); + }, + darker: function(k) { + return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity); + }, + rgb: function() { + var y = (this.l + 16) / 116, + x = isNaN(this.a) ? y : y + this.a / 500, + z = isNaN(this.b) ? y : y - this.b / 200; + y = Yn * lab2xyz(y); + x = Xn * lab2xyz(x); + z = Zn * lab2xyz(z); + return new Rgb( + xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB + xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z), + xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z), + this.opacity + ); + } +})); + +function xyz2lab(t) { + return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0; +} + +function lab2xyz(t) { + return t > t1 ? t * t * t : t2 * (t - t0); +} + +function xyz2rgb(x) { + return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055); +} + +function rgb2xyz(x) { + return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); +} + +function hclConvert(o) { + if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity); + if (!(o instanceof Lab)) o = labConvert(o); + var h = Math.atan2(o.b, o.a) * rad2deg; + return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity); +} + +function hcl(h, c, l, opacity) { + return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity); +} + +function Hcl(h, c, l, opacity) { + this.h = +h; + this.c = +c; + this.l = +l; + this.opacity = +opacity; +} + +define(Hcl, hcl, extend(Color, { + brighter: function(k) { + return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity); + }, + darker: function(k) { + return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity); + }, + rgb: function() { + return labConvert(this).rgb(); + } +})); + +var A = -0.14861; +var B = +1.78277; +var C = -0.29227; +var D = -0.90649; +var E = +1.97294; +var ED = E * D; +var EB = E * B; +var BC_DA = B * C - D * A; + +function cubehelixConvert(o) { + if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity); + if (!(o instanceof Rgb)) o = rgbConvert(o); + var r = o.r / 255, + g = o.g / 255, + b = o.b / 255, + l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB), + bl = b - l, + k = (E * (g - l) - C * bl) / D, + s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1 + h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN; + return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity); +} + +function cubehelix(h, s, l, opacity) { + return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity); +} + +function Cubehelix(h, s, l, opacity) { + this.h = +h; + this.s = +s; + this.l = +l; + this.opacity = +opacity; +} + +define(Cubehelix, cubehelix, extend(Color, { + brighter: function(k) { + k = k == null ? brighter : Math.pow(brighter, k); + return new Cubehelix(this.h, this.s, this.l * k, this.opacity); + }, + darker: function(k) { + k = k == null ? darker : Math.pow(darker, k); + return new Cubehelix(this.h, this.s, this.l * k, this.opacity); + }, + rgb: function() { + var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad, + l = +this.l, + a = isNaN(this.s) ? 0 : this.s * l * (1 - l), + cosh = Math.cos(h), + sinh = Math.sin(h); + return new Rgb( + 255 * (l + a * (A * cosh + B * sinh)), + 255 * (l + a * (C * cosh + D * sinh)), + 255 * (l + a * (E * cosh)), + this.opacity + ); + } +})); + +function basis(t1, v0, v1, v2, v3) { + var t2 = t1 * t1, t3 = t2 * t1; + return ((1 - 3 * t1 + 3 * t2 - t3) * v0 + + (4 - 6 * t2 + 3 * t3) * v1 + + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2 + + t3 * v3) / 6; +} + +function basis$1(values) { + var n = values.length - 1; + return function(t) { + var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n), + v1 = values[i], + v2 = values[i + 1], + v0 = i > 0 ? values[i - 1] : 2 * v1 - v2, + v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1; + return basis((t - i / n) * n, v0, v1, v2, v3); + }; +} + +function basisClosed(values) { + var n = values.length; + return function(t) { + var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n), + v0 = values[(i + n - 1) % n], + v1 = values[i % n], + v2 = values[(i + 1) % n], + v3 = values[(i + 2) % n]; + return basis((t - i / n) * n, v0, v1, v2, v3); + }; +} + +function constant$3(x) { + return function() { + return x; + }; +} + +function linear(a, d) { + return function(t) { + return a + t * d; + }; +} + +function exponential(a, b, y) { + return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) { + return Math.pow(a + t * b, y); + }; +} + +function hue(a, b) { + var d = b - a; + return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$3(isNaN(a) ? b : a); +} + +function gamma(y) { + return (y = +y) === 1 ? nogamma : function(a, b) { + return b - a ? exponential(a, b, y) : constant$3(isNaN(a) ? b : a); + }; +} + +function nogamma(a, b) { + var d = b - a; + return d ? linear(a, d) : constant$3(isNaN(a) ? b : a); +} + +var interpolateRgb = (function rgbGamma(y) { + var color$$1 = gamma(y); + + function rgb$$1(start, end) { + var r = color$$1((start = rgb(start)).r, (end = rgb(end)).r), + g = color$$1(start.g, end.g), + b = color$$1(start.b, end.b), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.r = r(t); + start.g = g(t); + start.b = b(t); + start.opacity = opacity(t); + return start + ""; + }; + } + + rgb$$1.gamma = rgbGamma; + + return rgb$$1; +})(1); + +function rgbSpline(spline) { + return function(colors) { + var n = colors.length, + r = new Array(n), + g = new Array(n), + b = new Array(n), + i, color$$1; + for (i = 0; i < n; ++i) { + color$$1 = rgb(colors[i]); + r[i] = color$$1.r || 0; + g[i] = color$$1.g || 0; + b[i] = color$$1.b || 0; + } + r = spline(r); + g = spline(g); + b = spline(b); + color$$1.opacity = 1; + return function(t) { + color$$1.r = r(t); + color$$1.g = g(t); + color$$1.b = b(t); + return color$$1 + ""; + }; + }; +} + +var rgbBasis = rgbSpline(basis$1); +var rgbBasisClosed = rgbSpline(basisClosed); + +function array$1(a, b) { + var nb = b ? b.length : 0, + na = a ? Math.min(nb, a.length) : 0, + x = new Array(na), + c = new Array(nb), + i; + + for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]); + for (; i < nb; ++i) c[i] = b[i]; + + return function(t) { + for (i = 0; i < na; ++i) c[i] = x[i](t); + return c; + }; +} + +function date(a, b) { + var d = new Date; + return a = +a, b -= a, function(t) { + return d.setTime(a + b * t), d; + }; +} + +function reinterpolate(a, b) { + return a = +a, b -= a, function(t) { + return a + b * t; + }; +} + +function object(a, b) { + var i = {}, + c = {}, + k; + + if (a === null || typeof a !== "object") a = {}; + if (b === null || typeof b !== "object") b = {}; + + for (k in b) { + if (k in a) { + i[k] = interpolateValue(a[k], b[k]); + } else { + c[k] = b[k]; + } + } + + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; +} + +var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; +var reB = new RegExp(reA.source, "g"); + +function zero(b) { + return function() { + return b; + }; +} + +function one(b) { + return function(t) { + return b(t) + ""; + }; +} + +function interpolateString(a, b) { + var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b + am, // current match in a + bm, // current match in b + bs, // string preceding current number in b, if any + i = -1, // index in s + s = [], // string constants and placeholders + q = []; // number interpolators + + // Coerce inputs to strings. + a = a + "", b = b + ""; + + // Interpolate pairs of numbers in a & b. + while ((am = reA.exec(a)) + && (bm = reB.exec(b))) { + if ((bs = bm.index) > bi) { // a string precedes the next number in b + bs = b.slice(bi, bs); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; + } + if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match + if (s[i]) s[i] += bm; // coalesce with previous string + else s[++i] = bm; + } else { // interpolate non-matching numbers + s[++i] = null; + q.push({i: i, x: reinterpolate(am, bm)}); + } + bi = reB.lastIndex; + } + + // Add remains of b. + if (bi < b.length) { + bs = b.slice(bi); + if (s[i]) s[i] += bs; // coalesce with previous string + else s[++i] = bs; + } + + // Special optimization for only a single match. + // Otherwise, interpolate each of the numbers and rejoin the string. + return s.length < 2 ? (q[0] + ? one(q[0].x) + : zero(b)) + : (b = q.length, function(t) { + for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }); +} + +function interpolateValue(a, b) { + var t = typeof b, c; + return b == null || t === "boolean" ? constant$3(b) + : (t === "number" ? reinterpolate + : t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString) + : b instanceof color ? interpolateRgb + : b instanceof Date ? date + : Array.isArray(b) ? array$1 + : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object + : reinterpolate)(a, b); +} + +function interpolateRound(a, b) { + return a = +a, b -= a, function(t) { + return Math.round(a + b * t); + }; +} + +var degrees = 180 / Math.PI; + +var identity$2 = { + translateX: 0, + translateY: 0, + rotate: 0, + skewX: 0, + scaleX: 1, + scaleY: 1 +}; + +function decompose(a, b, c, d, e, f) { + var scaleX, scaleY, skewX; + if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX; + if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX; + if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY; + if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; + return { + translateX: e, + translateY: f, + rotate: Math.atan2(b, a) * degrees, + skewX: Math.atan(skewX) * degrees, + scaleX: scaleX, + scaleY: scaleY + }; +} + +var cssNode; +var cssRoot; +var cssView; +var svgNode; + +function parseCss(value) { + if (value === "none") return identity$2; + if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView; + cssNode.style.transform = value; + value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform"); + cssRoot.removeChild(cssNode); + value = value.slice(7, -1).split(","); + return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]); +} + +function parseSvg(value) { + if (value == null) return identity$2; + if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); + svgNode.setAttribute("transform", value); + if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2; + value = value.matrix; + return decompose(value.a, value.b, value.c, value.d, value.e, value.f); +} + +function interpolateTransform(parse, pxComma, pxParen, degParen) { + + function pop(s) { + return s.length ? s.pop() + " " : ""; + } + + function translate(xa, ya, xb, yb, s, q) { + if (xa !== xb || ya !== yb) { + var i = s.push("translate(", null, pxComma, null, pxParen); + q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)}); + } else if (xb || yb) { + s.push("translate(" + xb + pxComma + yb + pxParen); + } + } + + function rotate(a, b, s, q) { + if (a !== b) { + if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path + q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: reinterpolate(a, b)}); + } else if (b) { + s.push(pop(s) + "rotate(" + b + degParen); + } + } + + function skewX(a, b, s, q) { + if (a !== b) { + q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: reinterpolate(a, b)}); + } else if (b) { + s.push(pop(s) + "skewX(" + b + degParen); + } + } + + function scale(xa, ya, xb, yb, s, q) { + if (xa !== xb || ya !== yb) { + var i = s.push(pop(s) + "scale(", null, ",", null, ")"); + q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)}); + } else if (xb !== 1 || yb !== 1) { + s.push(pop(s) + "scale(" + xb + "," + yb + ")"); + } + } + + return function(a, b) { + var s = [], // string constants and placeholders + q = []; // number interpolators + a = parse(a), b = parse(b); + translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q); + rotate(a.rotate, b.rotate, s, q); + skewX(a.skewX, b.skewX, s, q); + scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q); + a = b = null; // gc + return function(t) { + var i = -1, n = q.length, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + }; +} + +var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)"); +var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")"); + +var rho = Math.SQRT2; +var rho2 = 2; +var rho4 = 4; +var epsilon2 = 1e-12; + +function cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; +} + +function sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; +} + +function tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); +} + +// p0 = [ux0, uy0, w0] +// p1 = [ux1, uy1, w1] +function interpolateZoom(p0, p1) { + var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], + ux1 = p1[0], uy1 = p1[1], w1 = p1[2], + dx = ux1 - ux0, + dy = uy1 - uy0, + d2 = dx * dx + dy * dy, + i, + S; + + // Special case for u0 ≅ u1. + if (d2 < epsilon2) { + S = Math.log(w1 / w0) / rho; + i = function(t) { + return [ + ux0 + t * dx, + uy0 + t * dy, + w0 * Math.exp(rho * t * S) + ]; + }; + } + + // General case. + else { + var d1 = Math.sqrt(d2), + b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1), + b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1), + r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), + r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); + S = (r1 - r0) / rho; + i = function(t) { + var s = t * S, + coshr0 = cosh(r0), + u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0)); + return [ + ux0 + u * dx, + uy0 + u * dy, + w0 * coshr0 / cosh(rho * s + r0) + ]; + }; + } + + i.duration = S * 1000; + + return i; +} + +function hsl$1(hue$$1) { + return function(start, end) { + var h = hue$$1((start = hsl(start)).h, (end = hsl(end)).h), + s = nogamma(start.s, end.s), + l = nogamma(start.l, end.l), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.h = h(t); + start.s = s(t); + start.l = l(t); + start.opacity = opacity(t); + return start + ""; + }; + } +} + +var hsl$2 = hsl$1(hue); +var hslLong = hsl$1(nogamma); + +function lab$1(start, end) { + var l = nogamma((start = lab(start)).l, (end = lab(end)).l), + a = nogamma(start.a, end.a), + b = nogamma(start.b, end.b), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.l = l(t); + start.a = a(t); + start.b = b(t); + start.opacity = opacity(t); + return start + ""; + }; +} + +function hcl$1(hue$$1) { + return function(start, end) { + var h = hue$$1((start = hcl(start)).h, (end = hcl(end)).h), + c = nogamma(start.c, end.c), + l = nogamma(start.l, end.l), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.h = h(t); + start.c = c(t); + start.l = l(t); + start.opacity = opacity(t); + return start + ""; + }; + } +} + +var hcl$2 = hcl$1(hue); +var hclLong = hcl$1(nogamma); + +function cubehelix$1(hue$$1) { + return (function cubehelixGamma(y) { + y = +y; + + function cubehelix$$1(start, end) { + var h = hue$$1((start = cubehelix(start)).h, (end = cubehelix(end)).h), + s = nogamma(start.s, end.s), + l = nogamma(start.l, end.l), + opacity = nogamma(start.opacity, end.opacity); + return function(t) { + start.h = h(t); + start.s = s(t); + start.l = l(Math.pow(t, y)); + start.opacity = opacity(t); + return start + ""; + }; + } + + cubehelix$$1.gamma = cubehelixGamma; + + return cubehelix$$1; + })(1); +} + +var cubehelix$2 = cubehelix$1(hue); +var cubehelixLong = cubehelix$1(nogamma); + +function quantize(interpolator, n) { + var samples = new Array(n); + for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1)); + return samples; +} + +var frame = 0; +var timeout = 0; +var interval = 0; +var pokeDelay = 1000; +var taskHead; +var taskTail; +var clockLast = 0; +var clockNow = 0; +var clockSkew = 0; +var clock = typeof performance === "object" && performance.now ? performance : Date; +var setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); }; + +function now() { + return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); +} + +function clearNow() { + clockNow = 0; +} + +function Timer() { + this._call = + this._time = + this._next = null; +} + +Timer.prototype = timer.prototype = { + constructor: Timer, + restart: function(callback, delay, time) { + if (typeof callback !== "function") throw new TypeError("callback is not a function"); + time = (time == null ? now() : +time) + (delay == null ? 0 : +delay); + if (!this._next && taskTail !== this) { + if (taskTail) taskTail._next = this; + else taskHead = this; + taskTail = this; + } + this._call = callback; + this._time = time; + sleep(); + }, + stop: function() { + if (this._call) { + this._call = null; + this._time = Infinity; + sleep(); + } + } +}; + +function timer(callback, delay, time) { + var t = new Timer; + t.restart(callback, delay, time); + return t; +} + +function timerFlush() { + now(); // Get the current time, if not already set. + ++frame; // Pretend we’ve set an alarm, if we haven’t already. + var t = taskHead, e; + while (t) { + if ((e = clockNow - t._time) >= 0) t._call.call(null, e); + t = t._next; + } + --frame; +} + +function wake() { + clockNow = (clockLast = clock.now()) + clockSkew; + frame = timeout = 0; + try { + timerFlush(); + } finally { + frame = 0; + nap(); + clockNow = 0; + } +} + +function poke() { + var now = clock.now(), delay = now - clockLast; + if (delay > pokeDelay) clockSkew -= delay, clockLast = now; +} + +function nap() { + var t0, t1 = taskHead, t2, time = Infinity; + while (t1) { + if (t1._call) { + if (time > t1._time) time = t1._time; + t0 = t1, t1 = t1._next; + } else { + t2 = t1._next, t1._next = null; + t1 = t0 ? t0._next = t2 : taskHead = t2; + } + } + taskTail = t0; + sleep(time); +} + +function sleep(time) { + if (frame) return; // Soonest alarm already set, or will be. + if (timeout) timeout = clearTimeout(timeout); + var delay = time - clockNow; // Strictly less than if we recomputed clockNow. + if (delay > 24) { + if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew); + if (interval) interval = clearInterval(interval); + } else { + if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay); + frame = 1, setFrame(wake); + } +} + +function timeout$1(callback, delay, time) { + var t = new Timer; + delay = delay == null ? 0 : +delay; + t.restart(function(elapsed) { + t.stop(); + callback(elapsed + delay); + }, delay, time); + return t; +} + +function interval$1(callback, delay, time) { + var t = new Timer, total = delay; + if (delay == null) return t.restart(callback, delay, time), t; + delay = +delay, time = time == null ? now() : +time; + t.restart(function tick(elapsed) { + elapsed += total; + t.restart(tick, total += delay, time); + callback(elapsed); + }, delay, time); + return t; +} + +var emptyOn = dispatch("start", "end", "interrupt"); +var emptyTween = []; + +var CREATED = 0; +var SCHEDULED = 1; +var STARTING = 2; +var STARTED = 3; +var RUNNING = 4; +var ENDING = 5; +var ENDED = 6; + +function schedule(node, name, id, index, group, timing) { + var schedules = node.__transition; + if (!schedules) node.__transition = {}; + else if (id in schedules) return; + create(node, id, { + name: name, + index: index, // For context during callback. + group: group, // For context during callback. + on: emptyOn, + tween: emptyTween, + time: timing.time, + delay: timing.delay, + duration: timing.duration, + ease: timing.ease, + timer: null, + state: CREATED + }); +} + +function init(node, id) { + var schedule = get$1(node, id); + if (schedule.state > CREATED) throw new Error("too late; already scheduled"); + return schedule; +} + +function set$1(node, id) { + var schedule = get$1(node, id); + if (schedule.state > STARTING) throw new Error("too late; already started"); + return schedule; +} + +function get$1(node, id) { + var schedule = node.__transition; + if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found"); + return schedule; +} + +function create(node, id, self) { + var schedules = node.__transition, + tween; + + // Initialize the self timer when the transition is created. + // Note the actual delay is not known until the first callback! + schedules[id] = self; + self.timer = timer(schedule, 0, self.time); + + function schedule(elapsed) { + self.state = SCHEDULED; + self.timer.restart(start, self.delay, self.time); + + // If the elapsed delay is less than our first sleep, start immediately. + if (self.delay <= elapsed) start(elapsed - self.delay); + } + + function start(elapsed) { + var i, j, n, o; + + // If the state is not SCHEDULED, then we previously errored on start. + if (self.state !== SCHEDULED) return stop(); + + for (i in schedules) { + o = schedules[i]; + if (o.name !== self.name) continue; + + // While this element already has a starting transition during this frame, + // defer starting an interrupting transition until that transition has a + // chance to tick (and possibly end); see d3/d3-transition#54! + if (o.state === STARTED) return timeout$1(start); + + // Interrupt the active transition, if any. + // Dispatch the interrupt event. + if (o.state === RUNNING) { + o.state = ENDED; + o.timer.stop(); + o.on.call("interrupt", node, node.__data__, o.index, o.group); + delete schedules[i]; + } + + // Cancel any pre-empted transitions. No interrupt event is dispatched + // because the cancelled transitions never started. Note that this also + // removes this transition from the pending list! + else if (+i < id) { + o.state = ENDED; + o.timer.stop(); + delete schedules[i]; + } + } + + // Defer the first tick to end of the current frame; see d3/d3#1576. + // Note the transition may be canceled after start and before the first tick! + // Note this must be scheduled before the start event; see d3/d3-transition#16! + // Assuming this is successful, subsequent callbacks go straight to tick. + timeout$1(function() { + if (self.state === STARTED) { + self.state = RUNNING; + self.timer.restart(tick, self.delay, self.time); + tick(elapsed); + } + }); + + // Dispatch the start event. + // Note this must be done before the tween are initialized. + self.state = STARTING; + self.on.call("start", node, node.__data__, self.index, self.group); + if (self.state !== STARTING) return; // interrupted + self.state = STARTED; + + // Initialize the tween, deleting null tween. + tween = new Array(n = self.tween.length); + for (i = 0, j = -1; i < n; ++i) { + if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { + tween[++j] = o; + } + } + tween.length = j + 1; + } + + function tick(elapsed) { + var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), + i = -1, + n = tween.length; + + while (++i < n) { + tween[i].call(null, t); + } + + // Dispatch the end event. + if (self.state === ENDING) { + self.on.call("end", node, node.__data__, self.index, self.group); + stop(); + } + } + + function stop() { + self.state = ENDED; + self.timer.stop(); + delete schedules[id]; + for (var i in schedules) return; // eslint-disable-line no-unused-vars + delete node.__transition; + } +} + +function interrupt(node, name) { + var schedules = node.__transition, + schedule$$1, + active, + empty = true, + i; + + if (!schedules) return; + + name = name == null ? null : name + ""; + + for (i in schedules) { + if ((schedule$$1 = schedules[i]).name !== name) { empty = false; continue; } + active = schedule$$1.state > STARTING && schedule$$1.state < ENDING; + schedule$$1.state = ENDED; + schedule$$1.timer.stop(); + if (active) schedule$$1.on.call("interrupt", node, node.__data__, schedule$$1.index, schedule$$1.group); + delete schedules[i]; + } + + if (empty) delete node.__transition; +} + +function selection_interrupt(name) { + return this.each(function() { + interrupt(this, name); + }); +} + +function tweenRemove(id, name) { + var tween0, tween1; + return function() { + var schedule$$1 = set$1(this, id), + tween = schedule$$1.tween; + + // If this node shared tween with the previous node, + // just assign the updated shared tween and we’re done! + // Otherwise, copy-on-write. + if (tween !== tween0) { + tween1 = tween0 = tween; + for (var i = 0, n = tween1.length; i < n; ++i) { + if (tween1[i].name === name) { + tween1 = tween1.slice(); + tween1.splice(i, 1); + break; + } + } + } + + schedule$$1.tween = tween1; + }; +} + +function tweenFunction(id, name, value) { + var tween0, tween1; + if (typeof value !== "function") throw new Error; + return function() { + var schedule$$1 = set$1(this, id), + tween = schedule$$1.tween; + + // If this node shared tween with the previous node, + // just assign the updated shared tween and we’re done! + // Otherwise, copy-on-write. + if (tween !== tween0) { + tween1 = (tween0 = tween).slice(); + for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) { + if (tween1[i].name === name) { + tween1[i] = t; + break; + } + } + if (i === n) tween1.push(t); + } + + schedule$$1.tween = tween1; + }; +} + +function transition_tween(name, value) { + var id = this._id; + + name += ""; + + if (arguments.length < 2) { + var tween = get$1(this.node(), id).tween; + for (var i = 0, n = tween.length, t; i < n; ++i) { + if ((t = tween[i]).name === name) { + return t.value; + } + } + return null; + } + + return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value)); +} + +function tweenValue(transition, name, value) { + var id = transition._id; + + transition.each(function() { + var schedule$$1 = set$1(this, id); + (schedule$$1.value || (schedule$$1.value = {}))[name] = value.apply(this, arguments); + }); + + return function(node) { + return get$1(node, id).value[name]; + }; +} + +function interpolate(a, b) { + var c; + return (typeof b === "number" ? reinterpolate + : b instanceof color ? interpolateRgb + : (c = color(b)) ? (b = c, interpolateRgb) + : interpolateString)(a, b); +} + +function attrRemove$1(name) { + return function() { + this.removeAttribute(name); + }; +} + +function attrRemoveNS$1(fullname) { + return function() { + this.removeAttributeNS(fullname.space, fullname.local); + }; +} + +function attrConstant$1(name, interpolate$$1, value1) { + var value00, + interpolate0; + return function() { + var value0 = this.getAttribute(name); + return value0 === value1 ? null + : value0 === value00 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value1); + }; +} + +function attrConstantNS$1(fullname, interpolate$$1, value1) { + var value00, + interpolate0; + return function() { + var value0 = this.getAttributeNS(fullname.space, fullname.local); + return value0 === value1 ? null + : value0 === value00 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value1); + }; +} + +function attrFunction$1(name, interpolate$$1, value) { + var value00, + value10, + interpolate0; + return function() { + var value0, value1 = value(this); + if (value1 == null) return void this.removeAttribute(name); + value0 = this.getAttribute(name); + return value0 === value1 ? null + : value0 === value00 && value1 === value10 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); + }; +} + +function attrFunctionNS$1(fullname, interpolate$$1, value) { + var value00, + value10, + interpolate0; + return function() { + var value0, value1 = value(this); + if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local); + value0 = this.getAttributeNS(fullname.space, fullname.local); + return value0 === value1 ? null + : value0 === value00 && value1 === value10 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); + }; +} + +function transition_attr(name, value) { + var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate; + return this.attrTween(name, typeof value === "function" + ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value)) + : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname) + : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value + "")); +} + +function attrTweenNS(fullname, value) { + function tween() { + var node = this, i = value.apply(node, arguments); + return i && function(t) { + node.setAttributeNS(fullname.space, fullname.local, i(t)); + }; + } + tween._value = value; + return tween; +} + +function attrTween(name, value) { + function tween() { + var node = this, i = value.apply(node, arguments); + return i && function(t) { + node.setAttribute(name, i(t)); + }; + } + tween._value = value; + return tween; +} + +function transition_attrTween(name, value) { + var key = "attr." + name; + if (arguments.length < 2) return (key = this.tween(key)) && key._value; + if (value == null) return this.tween(key, null); + if (typeof value !== "function") throw new Error; + var fullname = namespace(name); + return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value)); +} + +function delayFunction(id, value) { + return function() { + init(this, id).delay = +value.apply(this, arguments); + }; +} + +function delayConstant(id, value) { + return value = +value, function() { + init(this, id).delay = value; + }; +} + +function transition_delay(value) { + var id = this._id; + + return arguments.length + ? this.each((typeof value === "function" + ? delayFunction + : delayConstant)(id, value)) + : get$1(this.node(), id).delay; +} + +function durationFunction(id, value) { + return function() { + set$1(this, id).duration = +value.apply(this, arguments); + }; +} + +function durationConstant(id, value) { + return value = +value, function() { + set$1(this, id).duration = value; + }; +} + +function transition_duration(value) { + var id = this._id; + + return arguments.length + ? this.each((typeof value === "function" + ? durationFunction + : durationConstant)(id, value)) + : get$1(this.node(), id).duration; +} + +function easeConstant(id, value) { + if (typeof value !== "function") throw new Error; + return function() { + set$1(this, id).ease = value; + }; +} + +function transition_ease(value) { + var id = this._id; + + return arguments.length + ? this.each(easeConstant(id, value)) + : get$1(this.node(), id).ease; +} + +function transition_filter(match) { + if (typeof match !== "function") match = matcher$1(match); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { + if ((node = group[i]) && match.call(node, node.__data__, i, group)) { + subgroup.push(node); + } + } + } + + return new Transition(subgroups, this._parents, this._name, this._id); +} + +function transition_merge(transition$$1) { + if (transition$$1._id !== this._id) throw new Error; + + for (var groups0 = this._groups, groups1 = transition$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { + for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { + if (node = group0[i] || group1[i]) { + merge[i] = node; + } + } + } + + for (; j < m0; ++j) { + merges[j] = groups0[j]; + } + + return new Transition(merges, this._parents, this._name, this._id); +} + +function start(name) { + return (name + "").trim().split(/^|\s+/).every(function(t) { + var i = t.indexOf("."); + if (i >= 0) t = t.slice(0, i); + return !t || t === "start"; + }); +} + +function onFunction(id, name, listener) { + var on0, on1, sit = start(name) ? init : set$1; + return function() { + var schedule$$1 = sit(this, id), + on = schedule$$1.on; + + // If this node shared a dispatch with the previous node, + // just assign the updated shared dispatch and we’re done! + // Otherwise, copy-on-write. + if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener); + + schedule$$1.on = on1; + }; +} + +function transition_on(name, listener) { + var id = this._id; + + return arguments.length < 2 + ? get$1(this.node(), id).on.on(name) + : this.each(onFunction(id, name, listener)); +} + +function removeFunction(id) { + return function() { + var parent = this.parentNode; + for (var i in this.__transition) if (+i !== id) return; + if (parent) parent.removeChild(this); + }; +} + +function transition_remove() { + return this.on("end.remove", removeFunction(this._id)); +} + +function transition_select(select) { + var name = this._name, + id = this._id; + + if (typeof select !== "function") select = selector(select); + + for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { + if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + subgroup[i] = subnode; + schedule(subgroup[i], name, id, i, subgroup, get$1(node, id)); + } + } + } + + return new Transition(subgroups, this._parents, name, id); +} + +function transition_selectAll(select) { + var name = this._name, + id = this._id; + + if (typeof select !== "function") select = selectorAll(select); + + for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + for (var children = select.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) { + if (child = children[k]) { + schedule(child, name, id, k, children, inherit); + } + } + subgroups.push(children); + parents.push(node); + } + } + } + + return new Transition(subgroups, parents, name, id); +} + +var Selection$1 = selection.prototype.constructor; + +function transition_selection() { + return new Selection$1(this._groups, this._parents); +} + +function styleRemove$1(name, interpolate$$1) { + var value00, + value10, + interpolate0; + return function() { + var value0 = styleValue(this, name), + value1 = (this.style.removeProperty(name), styleValue(this, name)); + return value0 === value1 ? null + : value0 === value00 && value1 === value10 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); + }; +} + +function styleRemoveEnd(name) { + return function() { + this.style.removeProperty(name); + }; +} + +function styleConstant$1(name, interpolate$$1, value1) { + var value00, + interpolate0; + return function() { + var value0 = styleValue(this, name); + return value0 === value1 ? null + : value0 === value00 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value1); + }; +} + +function styleFunction$1(name, interpolate$$1, value) { + var value00, + value10, + interpolate0; + return function() { + var value0 = styleValue(this, name), + value1 = value(this); + if (value1 == null) value1 = (this.style.removeProperty(name), styleValue(this, name)); + return value0 === value1 ? null + : value0 === value00 && value1 === value10 ? interpolate0 + : interpolate0 = interpolate$$1(value00 = value0, value10 = value1); + }; +} + +function transition_style(name, value, priority) { + var i = (name += "") === "transform" ? interpolateTransformCss : interpolate; + return value == null ? this + .styleTween(name, styleRemove$1(name, i)) + .on("end.style." + name, styleRemoveEnd(name)) + : this.styleTween(name, typeof value === "function" + ? styleFunction$1(name, i, tweenValue(this, "style." + name, value)) + : styleConstant$1(name, i, value + ""), priority); +} + +function styleTween(name, value, priority) { + function tween() { + var node = this, i = value.apply(node, arguments); + return i && function(t) { + node.style.setProperty(name, i(t), priority); + }; + } + tween._value = value; + return tween; +} + +function transition_styleTween(name, value, priority) { + var key = "style." + (name += ""); + if (arguments.length < 2) return (key = this.tween(key)) && key._value; + if (value == null) return this.tween(key, null); + if (typeof value !== "function") throw new Error; + return this.tween(key, styleTween(name, value, priority == null ? "" : priority)); +} + +function textConstant$1(value) { + return function() { + this.textContent = value; + }; +} + +function textFunction$1(value) { + return function() { + var value1 = value(this); + this.textContent = value1 == null ? "" : value1; + }; +} + +function transition_text(value) { + return this.tween("text", typeof value === "function" + ? textFunction$1(tweenValue(this, "text", value)) + : textConstant$1(value == null ? "" : value + "")); +} + +function transition_transition() { + var name = this._name, + id0 = this._id, + id1 = newId(); + + for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + var inherit = get$1(node, id0); + schedule(node, name, id1, i, group, { + time: inherit.time + inherit.delay + inherit.duration, + delay: 0, + duration: inherit.duration, + ease: inherit.ease + }); + } + } + } + + return new Transition(groups, this._parents, name, id1); +} + +var id = 0; + +function Transition(groups, parents, name, id) { + this._groups = groups; + this._parents = parents; + this._name = name; + this._id = id; +} + +function transition(name) { + return selection().transition(name); +} + +function newId() { + return ++id; +} + +var selection_prototype = selection.prototype; + +Transition.prototype = transition.prototype = { + constructor: Transition, + select: transition_select, + selectAll: transition_selectAll, + filter: transition_filter, + merge: transition_merge, + selection: transition_selection, + transition: transition_transition, + call: selection_prototype.call, + nodes: selection_prototype.nodes, + node: selection_prototype.node, + size: selection_prototype.size, + empty: selection_prototype.empty, + each: selection_prototype.each, + on: transition_on, + attr: transition_attr, + attrTween: transition_attrTween, + style: transition_style, + styleTween: transition_styleTween, + text: transition_text, + remove: transition_remove, + tween: transition_tween, + delay: transition_delay, + duration: transition_duration, + ease: transition_ease +}; + +function linear$1(t) { + return +t; +} + +function quadIn(t) { + return t * t; +} + +function quadOut(t) { + return t * (2 - t); +} + +function quadInOut(t) { + return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2; +} + +function cubicIn(t) { + return t * t * t; +} + +function cubicOut(t) { + return --t * t * t + 1; +} + +function cubicInOut(t) { + return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; +} + +var exponent = 3; + +var polyIn = (function custom(e) { + e = +e; + + function polyIn(t) { + return Math.pow(t, e); + } + + polyIn.exponent = custom; + + return polyIn; +})(exponent); + +var polyOut = (function custom(e) { + e = +e; + + function polyOut(t) { + return 1 - Math.pow(1 - t, e); + } + + polyOut.exponent = custom; + + return polyOut; +})(exponent); + +var polyInOut = (function custom(e) { + e = +e; + + function polyInOut(t) { + return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2; + } + + polyInOut.exponent = custom; + + return polyInOut; +})(exponent); + +var pi = Math.PI; +var halfPi = pi / 2; + +function sinIn(t) { + return 1 - Math.cos(t * halfPi); +} + +function sinOut(t) { + return Math.sin(t * halfPi); +} + +function sinInOut(t) { + return (1 - Math.cos(pi * t)) / 2; +} + +function expIn(t) { + return Math.pow(2, 10 * t - 10); +} + +function expOut(t) { + return 1 - Math.pow(2, -10 * t); +} + +function expInOut(t) { + return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2; +} + +function circleIn(t) { + return 1 - Math.sqrt(1 - t * t); +} + +function circleOut(t) { + return Math.sqrt(1 - --t * t); +} + +function circleInOut(t) { + return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2; +} + +var b1 = 4 / 11; +var b2 = 6 / 11; +var b3 = 8 / 11; +var b4 = 3 / 4; +var b5 = 9 / 11; +var b6 = 10 / 11; +var b7 = 15 / 16; +var b8 = 21 / 22; +var b9 = 63 / 64; +var b0 = 1 / b1 / b1; + +function bounceIn(t) { + return 1 - bounceOut(1 - t); +} + +function bounceOut(t) { + return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9; +} + +function bounceInOut(t) { + return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2; +} + +var overshoot = 1.70158; + +var backIn = (function custom(s) { + s = +s; + + function backIn(t) { + return t * t * ((s + 1) * t - s); + } + + backIn.overshoot = custom; + + return backIn; +})(overshoot); + +var backOut = (function custom(s) { + s = +s; + + function backOut(t) { + return --t * t * ((s + 1) * t + s) + 1; + } + + backOut.overshoot = custom; + + return backOut; +})(overshoot); + +var backInOut = (function custom(s) { + s = +s; + + function backInOut(t) { + return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2; + } + + backInOut.overshoot = custom; + + return backInOut; +})(overshoot); + +var tau = 2 * Math.PI; +var amplitude = 1; +var period = 0.3; + +var elasticIn = (function custom(a, p) { + var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); + + function elasticIn(t) { + return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p); + } + + elasticIn.amplitude = function(a) { return custom(a, p * tau); }; + elasticIn.period = function(p) { return custom(a, p); }; + + return elasticIn; +})(amplitude, period); + +var elasticOut = (function custom(a, p) { + var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); + + function elasticOut(t) { + return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p); + } + + elasticOut.amplitude = function(a) { return custom(a, p * tau); }; + elasticOut.period = function(p) { return custom(a, p); }; + + return elasticOut; +})(amplitude, period); + +var elasticInOut = (function custom(a, p) { + var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau); + + function elasticInOut(t) { + return ((t = t * 2 - 1) < 0 + ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p) + : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2; + } + + elasticInOut.amplitude = function(a) { return custom(a, p * tau); }; + elasticInOut.period = function(p) { return custom(a, p); }; + + return elasticInOut; +})(amplitude, period); + +var defaultTiming = { + time: null, // Set on use. + delay: 0, + duration: 250, + ease: cubicInOut +}; + +function inherit(node, id) { + var timing; + while (!(timing = node.__transition) || !(timing = timing[id])) { + if (!(node = node.parentNode)) { + return defaultTiming.time = now(), defaultTiming; + } + } + return timing; +} + +function selection_transition(name) { + var id, + timing; + + if (name instanceof Transition) { + id = name._id, name = name._name; + } else { + id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + ""; + } + + for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { + for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { + if (node = group[i]) { + schedule(node, name, id, i, group, timing || inherit(node, id)); + } + } + } + + return new Transition(groups, this._parents, name, id); +} + +selection.prototype.interrupt = selection_interrupt; +selection.prototype.transition = selection_transition; + +var root$1 = [null]; + +function active(node, name) { + var schedules = node.__transition, + schedule$$1, + i; + + if (schedules) { + name = name == null ? null : name + ""; + for (i in schedules) { + if ((schedule$$1 = schedules[i]).state > SCHEDULED && schedule$$1.name === name) { + return new Transition([[node]], root$1, name, +i); + } + } + } + + return null; +} + +function constant$4(x) { + return function() { + return x; + }; +} + +function BrushEvent(target, type, selection) { + this.target = target; + this.type = type; + this.selection = selection; +} + +function nopropagation$1() { + exports.event.stopImmediatePropagation(); +} + +function noevent$1() { + exports.event.preventDefault(); + exports.event.stopImmediatePropagation(); +} + +var MODE_DRAG = {name: "drag"}; +var MODE_SPACE = {name: "space"}; +var MODE_HANDLE = {name: "handle"}; +var MODE_CENTER = {name: "center"}; + +var X = { + name: "x", + handles: ["e", "w"].map(type), + input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; }, + output: function(xy) { return xy && [xy[0][0], xy[1][0]]; } +}; + +var Y = { + name: "y", + handles: ["n", "s"].map(type), + input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; }, + output: function(xy) { return xy && [xy[0][1], xy[1][1]]; } +}; + +var XY = { + name: "xy", + handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type), + input: function(xy) { return xy; }, + output: function(xy) { return xy; } +}; + +var cursors = { + overlay: "crosshair", + selection: "move", + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" +}; + +var flipX = { + e: "w", + w: "e", + nw: "ne", + ne: "nw", + se: "sw", + sw: "se" +}; + +var flipY = { + n: "s", + s: "n", + nw: "sw", + ne: "se", + se: "ne", + sw: "nw" +}; + +var signsX = { + overlay: +1, + selection: +1, + n: null, + e: +1, + s: null, + w: -1, + nw: -1, + ne: +1, + se: +1, + sw: -1 +}; + +var signsY = { + overlay: +1, + selection: +1, + n: -1, + e: null, + s: +1, + w: null, + nw: -1, + ne: -1, + se: +1, + sw: +1 +}; + +function type(t) { + return {type: t}; +} + +// Ignore right-click, since that should open the context menu. +function defaultFilter() { + return !exports.event.button; +} + +function defaultExtent() { + var svg = this.ownerSVGElement || this; + return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]]; +} + +// Like d3.local, but with the name “__brush” rather than auto-generated. +function local(node) { + while (!node.__brush) if (!(node = node.parentNode)) return; + return node.__brush; +} + +function empty(extent) { + return extent[0][0] === extent[1][0] + || extent[0][1] === extent[1][1]; +} + +function brushSelection(node) { + var state = node.__brush; + return state ? state.dim.output(state.selection) : null; +} + +function brushX() { + return brush$1(X); +} + +function brushY() { + return brush$1(Y); +} + +function brush() { + return brush$1(XY); +} + +function brush$1(dim) { + var extent = defaultExtent, + filter = defaultFilter, + listeners = dispatch(brush, "start", "brush", "end"), + handleSize = 6, + touchending; + + function brush(group) { + var overlay = group + .property("__brush", initialize) + .selectAll(".overlay") + .data([type("overlay")]); + + overlay.enter().append("rect") + .attr("class", "overlay") + .attr("pointer-events", "all") + .attr("cursor", cursors.overlay) + .merge(overlay) + .each(function() { + var extent = local(this).extent; + select(this) + .attr("x", extent[0][0]) + .attr("y", extent[0][1]) + .attr("width", extent[1][0] - extent[0][0]) + .attr("height", extent[1][1] - extent[0][1]); + }); + + group.selectAll(".selection") + .data([type("selection")]) + .enter().append("rect") + .attr("class", "selection") + .attr("cursor", cursors.selection) + .attr("fill", "#777") + .attr("fill-opacity", 0.3) + .attr("stroke", "#fff") + .attr("shape-rendering", "crispEdges"); + + var handle = group.selectAll(".handle") + .data(dim.handles, function(d) { return d.type; }); + + handle.exit().remove(); + + handle.enter().append("rect") + .attr("class", function(d) { return "handle handle--" + d.type; }) + .attr("cursor", function(d) { return cursors[d.type]; }); + + group + .each(redraw) + .attr("fill", "none") + .attr("pointer-events", "all") + .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)") + .on("mousedown.brush touchstart.brush", started); + } + + brush.move = function(group, selection) { + if (group.selection) { + group + .on("start.brush", function() { emitter(this, arguments).beforestart().start(); }) + .on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); }) + .tween("brush", function() { + var that = this, + state = that.__brush, + emit = emitter(that, arguments), + selection0 = state.selection, + selection1 = dim.input(typeof selection === "function" ? selection.apply(this, arguments) : selection, state.extent), + i = interpolateValue(selection0, selection1); + + function tween(t) { + state.selection = t === 1 && empty(selection1) ? null : i(t); + redraw.call(that); + emit.brush(); + } + + return selection0 && selection1 ? tween : tween(1); + }); + } else { + group + .each(function() { + var that = this, + args = arguments, + state = that.__brush, + selection1 = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent), + emit = emitter(that, args).beforestart(); + + interrupt(that); + state.selection = selection1 == null || empty(selection1) ? null : selection1; + redraw.call(that); + emit.start().brush().end(); + }); + } + }; + + function redraw() { + var group = select(this), + selection = local(this).selection; + + if (selection) { + group.selectAll(".selection") + .style("display", null) + .attr("x", selection[0][0]) + .attr("y", selection[0][1]) + .attr("width", selection[1][0] - selection[0][0]) + .attr("height", selection[1][1] - selection[0][1]); + + group.selectAll(".handle") + .style("display", null) + .attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; }) + .attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; }) + .attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + handleSize : handleSize; }) + .attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + handleSize : handleSize; }); + } + + else { + group.selectAll(".selection,.handle") + .style("display", "none") + .attr("x", null) + .attr("y", null) + .attr("width", null) + .attr("height", null); + } + } + + function emitter(that, args) { + return that.__brush.emitter || new Emitter(that, args); + } + + function Emitter(that, args) { + this.that = that; + this.args = args; + this.state = that.__brush; + this.active = 0; + } + + Emitter.prototype = { + beforestart: function() { + if (++this.active === 1) this.state.emitter = this, this.starting = true; + return this; + }, + start: function() { + if (this.starting) this.starting = false, this.emit("start"); + return this; + }, + brush: function() { + this.emit("brush"); + return this; + }, + end: function() { + if (--this.active === 0) delete this.state.emitter, this.emit("end"); + return this; + }, + emit: function(type) { + customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]); + } + }; + + function started() { + if (exports.event.touches) { if (exports.event.changedTouches.length < exports.event.touches.length) return noevent$1(); } + else if (touchending) return; + if (!filter.apply(this, arguments)) return; + + var that = this, + type = exports.event.target.__data__.type, + mode = (exports.event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (exports.event.altKey ? MODE_CENTER : MODE_HANDLE), + signX = dim === Y ? null : signsX[type], + signY = dim === X ? null : signsY[type], + state = local(that), + extent = state.extent, + selection = state.selection, + W = extent[0][0], w0, w1, + N = extent[0][1], n0, n1, + E = extent[1][0], e0, e1, + S = extent[1][1], s0, s1, + dx, + dy, + moving, + shifting = signX && signY && exports.event.shiftKey, + lockX, + lockY, + point0 = mouse(that), + point = point0, + emit = emitter(that, arguments).beforestart(); + + if (type === "overlay") { + state.selection = selection = [ + [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]], + [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0] + ]; + } else { + w0 = selection[0][0]; + n0 = selection[0][1]; + e0 = selection[1][0]; + s0 = selection[1][1]; + } + + w1 = w0; + n1 = n0; + e1 = e0; + s1 = s0; + + var group = select(that) + .attr("pointer-events", "none"); + + var overlay = group.selectAll(".overlay") + .attr("cursor", cursors[type]); + + if (exports.event.touches) { + group + .on("touchmove.brush", moved, true) + .on("touchend.brush touchcancel.brush", ended, true); + } else { + var view = select(exports.event.view) + .on("keydown.brush", keydowned, true) + .on("keyup.brush", keyupped, true) + .on("mousemove.brush", moved, true) + .on("mouseup.brush", ended, true); + + dragDisable(exports.event.view); + } + + nopropagation$1(); + interrupt(that); + redraw.call(that); + emit.start(); + + function moved() { + var point1 = mouse(that); + if (shifting && !lockX && !lockY) { + if (Math.abs(point1[0] - point[0]) > Math.abs(point1[1] - point[1])) lockY = true; + else lockX = true; + } + point = point1; + moving = true; + noevent$1(); + move(); + } + + function move() { + var t; + + dx = point[0] - point0[0]; + dy = point[1] - point0[1]; + + switch (mode) { + case MODE_SPACE: + case MODE_DRAG: { + if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx; + if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy; + break; + } + case MODE_HANDLE: { + if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0; + else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx; + if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0; + else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy; + break; + } + case MODE_CENTER: { + if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX)); + if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY)); + break; + } + } + + if (e1 < w1) { + signX *= -1; + t = w0, w0 = e0, e0 = t; + t = w1, w1 = e1, e1 = t; + if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]); + } + + if (s1 < n1) { + signY *= -1; + t = n0, n0 = s0, s0 = t; + t = n1, n1 = s1, s1 = t; + if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]); + } + + if (state.selection) selection = state.selection; // May be set by brush.move! + if (lockX) w1 = selection[0][0], e1 = selection[1][0]; + if (lockY) n1 = selection[0][1], s1 = selection[1][1]; + + if (selection[0][0] !== w1 + || selection[0][1] !== n1 + || selection[1][0] !== e1 + || selection[1][1] !== s1) { + state.selection = [[w1, n1], [e1, s1]]; + redraw.call(that); + emit.brush(); + } + } + + function ended() { + nopropagation$1(); + if (exports.event.touches) { + if (exports.event.touches.length) return; + if (touchending) clearTimeout(touchending); + touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! + group.on("touchmove.brush touchend.brush touchcancel.brush", null); + } else { + yesdrag(exports.event.view, moving); + view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); + } + group.attr("pointer-events", "all"); + overlay.attr("cursor", cursors.overlay); + if (state.selection) selection = state.selection; // May be set by brush.move (on start)! + if (empty(selection)) state.selection = null, redraw.call(that); + emit.end(); + } + + function keydowned() { + switch (exports.event.keyCode) { + case 16: { // SHIFT + shifting = signX && signY; + break; + } + case 18: { // ALT + if (mode === MODE_HANDLE) { + if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; + if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; + mode = MODE_CENTER; + move(); + } + break; + } + case 32: { // SPACE; takes priority over ALT + if (mode === MODE_HANDLE || mode === MODE_CENTER) { + if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx; + if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy; + mode = MODE_SPACE; + overlay.attr("cursor", cursors.selection); + move(); + } + break; + } + default: return; + } + noevent$1(); + } + + function keyupped() { + switch (exports.event.keyCode) { + case 16: { // SHIFT + if (shifting) { + lockX = lockY = shifting = false; + move(); + } + break; + } + case 18: { // ALT + if (mode === MODE_CENTER) { + if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; + if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; + mode = MODE_HANDLE; + move(); + } + break; + } + case 32: { // SPACE + if (mode === MODE_SPACE) { + if (exports.event.altKey) { + if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; + if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; + mode = MODE_CENTER; + } else { + if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1; + if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; + mode = MODE_HANDLE; + } + overlay.attr("cursor", cursors[type]); + move(); + } + break; + } + default: return; + } + noevent$1(); + } + } + + function initialize() { + var state = this.__brush || {selection: null}; + state.extent = extent.apply(this, arguments); + state.dim = dim; + return state; + } + + brush.extent = function(_) { + return arguments.length ? (extent = typeof _ === "function" ? _ : constant$4([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent; + }; + + brush.filter = function(_) { + return arguments.length ? (filter = typeof _ === "function" ? _ : constant$4(!!_), brush) : filter; + }; + + brush.handleSize = function(_) { + return arguments.length ? (handleSize = +_, brush) : handleSize; + }; + + brush.on = function() { + var value = listeners.on.apply(listeners, arguments); + return value === listeners ? brush : value; + }; + + return brush; +} + +var cos = Math.cos; +var sin = Math.sin; +var pi$1 = Math.PI; +var halfPi$1 = pi$1 / 2; +var tau$1 = pi$1 * 2; +var max$1 = Math.max; + +function compareValue(compare) { + return function(a, b) { + return compare( + a.source.value + a.target.value, + b.source.value + b.target.value + ); + }; +} + +function chord() { + var padAngle = 0, + sortGroups = null, + sortSubgroups = null, + sortChords = null; + + function chord(matrix) { + var n = matrix.length, + groupSums = [], + groupIndex = sequence(n), + subgroupIndex = [], + chords = [], + groups = chords.groups = new Array(n), + subgroups = new Array(n * n), + k, + x, + x0, + dx, + i, + j; + + // Compute the sum. + k = 0, i = -1; while (++i < n) { + x = 0, j = -1; while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(sequence(n)); + k += x; + } + + // Sort groups… + if (sortGroups) groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + + // Sort subgroups… + if (sortSubgroups) subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + + // Convert the sum to scaling factor for [0, 2pi]. + // TODO Allow start and end angle to be specified? + // TODO Allow padding to be specified as percentage? + k = max$1(0, tau$1 - padAngle * n) / k; + dx = k ? padAngle : tau$1 / n; + + // Compute the start and end angle for each group and subgroup. + // Note: Opera has a bug reordering object literal properties! + x = 0, i = -1; while (++i < n) { + x0 = x, j = -1; while (++j < n) { + var di = groupIndex[i], + dj = subgroupIndex[di][j], + v = matrix[di][dj], + a0 = x, + a1 = x += v * k; + subgroups[dj * n + di] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: groupSums[di] + }; + x += dx; + } + + // Generate chords for each (non-empty) subgroup-subgroup link. + i = -1; while (++i < n) { + j = i - 1; while (++j < n) { + var source = subgroups[j * n + i], + target = subgroups[i * n + j]; + if (source.value || target.value) { + chords.push(source.value < target.value + ? {source: target, target: source} + : {source: source, target: target}); + } + } + } + + return sortChords ? chords.sort(sortChords) : chords; + } + + chord.padAngle = function(_) { + return arguments.length ? (padAngle = max$1(0, _), chord) : padAngle; + }; + + chord.sortGroups = function(_) { + return arguments.length ? (sortGroups = _, chord) : sortGroups; + }; + + chord.sortSubgroups = function(_) { + return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups; + }; + + chord.sortChords = function(_) { + return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._; + }; + + return chord; +} + +var slice$2 = Array.prototype.slice; + +function constant$5(x) { + return function() { + return x; + }; +} + +var pi$2 = Math.PI; +var tau$2 = 2 * pi$2; +var epsilon$1 = 1e-6; +var tauEpsilon = tau$2 - epsilon$1; + +function Path() { + this._x0 = this._y0 = // start of current subpath + this._x1 = this._y1 = null; // end of current subpath + this._ = ""; +} + +function path() { + return new Path; +} + +Path.prototype = path.prototype = { + constructor: Path, + moveTo: function(x, y) { + this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y); + }, + closePath: function() { + if (this._x1 !== null) { + this._x1 = this._x0, this._y1 = this._y0; + this._ += "Z"; + } + }, + lineTo: function(x, y) { + this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y); + }, + quadraticCurveTo: function(x1, y1, x, y) { + this._ += "Q" + (+x1) + "," + (+y1) + "," + (this._x1 = +x) + "," + (this._y1 = +y); + }, + bezierCurveTo: function(x1, y1, x2, y2, x, y) { + this._ += "C" + (+x1) + "," + (+y1) + "," + (+x2) + "," + (+y2) + "," + (this._x1 = +x) + "," + (this._y1 = +y); + }, + arcTo: function(x1, y1, x2, y2, r) { + x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r; + var x0 = this._x1, + y0 = this._y1, + x21 = x2 - x1, + y21 = y2 - y1, + x01 = x0 - x1, + y01 = y0 - y1, + l01_2 = x01 * x01 + y01 * y01; + + // Is the radius negative? Error. + if (r < 0) throw new Error("negative radius: " + r); + + // Is this path empty? Move to (x1,y1). + if (this._x1 === null) { + this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1); + } + + // Or, is (x1,y1) coincident with (x0,y0)? Do nothing. + else if (!(l01_2 > epsilon$1)) {} + + // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear? + // Equivalently, is (x1,y1) coincident with (x2,y2)? + // Or, is the radius zero? Line to (x1,y1). + else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon$1) || !r) { + this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1); + } + + // Otherwise, draw an arc! + else { + var x20 = x2 - x0, + y20 = y2 - y0, + l21_2 = x21 * x21 + y21 * y21, + l20_2 = x20 * x20 + y20 * y20, + l21 = Math.sqrt(l21_2), + l01 = Math.sqrt(l01_2), + l = r * Math.tan((pi$2 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2), + t01 = l / l01, + t21 = l / l21; + + // If the start tangent is not coincident with (x0,y0), line to. + if (Math.abs(t01 - 1) > epsilon$1) { + this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01); + } + + this._ += "A" + r + "," + r + ",0,0," + (+(y01 * x20 > x01 * y20)) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21); + } + }, + arc: function(x, y, r, a0, a1, ccw) { + x = +x, y = +y, r = +r; + var dx = r * Math.cos(a0), + dy = r * Math.sin(a0), + x0 = x + dx, + y0 = y + dy, + cw = 1 ^ ccw, + da = ccw ? a0 - a1 : a1 - a0; + + // Is the radius negative? Error. + if (r < 0) throw new Error("negative radius: " + r); + + // Is this path empty? Move to (x0,y0). + if (this._x1 === null) { + this._ += "M" + x0 + "," + y0; + } + + // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0). + else if (Math.abs(this._x1 - x0) > epsilon$1 || Math.abs(this._y1 - y0) > epsilon$1) { + this._ += "L" + x0 + "," + y0; + } + + // Is this arc empty? We’re done. + if (!r) return; + + // Does the angle go the wrong way? Flip the direction. + if (da < 0) da = da % tau$2 + tau$2; + + // Is this a complete circle? Draw two arcs to complete the circle. + if (da > tauEpsilon) { + this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0); + } + + // Is this arc non-empty? Draw an arc! + else if (da > epsilon$1) { + this._ += "A" + r + "," + r + ",0," + (+(da >= pi$2)) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1)); + } + }, + rect: function(x, y, w, h) { + this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + (+w) + "v" + (+h) + "h" + (-w) + "Z"; + }, + toString: function() { + return this._; + } +}; + +function defaultSource(d) { + return d.source; +} + +function defaultTarget(d) { + return d.target; +} + +function defaultRadius(d) { + return d.radius; +} + +function defaultStartAngle(d) { + return d.startAngle; +} + +function defaultEndAngle(d) { + return d.endAngle; +} + +function ribbon() { + var source = defaultSource, + target = defaultTarget, + radius = defaultRadius, + startAngle = defaultStartAngle, + endAngle = defaultEndAngle, + context = null; + + function ribbon() { + var buffer, + argv = slice$2.call(arguments), + s = source.apply(this, argv), + t = target.apply(this, argv), + sr = +radius.apply(this, (argv[0] = s, argv)), + sa0 = startAngle.apply(this, argv) - halfPi$1, + sa1 = endAngle.apply(this, argv) - halfPi$1, + sx0 = sr * cos(sa0), + sy0 = sr * sin(sa0), + tr = +radius.apply(this, (argv[0] = t, argv)), + ta0 = startAngle.apply(this, argv) - halfPi$1, + ta1 = endAngle.apply(this, argv) - halfPi$1; + + if (!context) context = buffer = path(); + + context.moveTo(sx0, sy0); + context.arc(0, 0, sr, sa0, sa1); + if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr? + context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0)); + context.arc(0, 0, tr, ta0, ta1); + } + context.quadraticCurveTo(0, 0, sx0, sy0); + context.closePath(); + + if (buffer) return context = null, buffer + "" || null; + } + + ribbon.radius = function(_) { + return arguments.length ? (radius = typeof _ === "function" ? _ : constant$5(+_), ribbon) : radius; + }; + + ribbon.startAngle = function(_) { + return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : startAngle; + }; + + ribbon.endAngle = function(_) { + return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : endAngle; + }; + + ribbon.source = function(_) { + return arguments.length ? (source = _, ribbon) : source; + }; + + ribbon.target = function(_) { + return arguments.length ? (target = _, ribbon) : target; + }; + + ribbon.context = function(_) { + return arguments.length ? (context = _ == null ? null : _, ribbon) : context; + }; + + return ribbon; +} + +var prefix = "$"; + +function Map() {} + +Map.prototype = map$1.prototype = { + constructor: Map, + has: function(key) { + return (prefix + key) in this; + }, + get: function(key) { + return this[prefix + key]; + }, + set: function(key, value) { + this[prefix + key] = value; + return this; + }, + remove: function(key) { + var property = prefix + key; + return property in this && delete this[property]; + }, + clear: function() { + for (var property in this) if (property[0] === prefix) delete this[property]; + }, + keys: function() { + var keys = []; + for (var property in this) if (property[0] === prefix) keys.push(property.slice(1)); + return keys; + }, + values: function() { + var values = []; + for (var property in this) if (property[0] === prefix) values.push(this[property]); + return values; + }, + entries: function() { + var entries = []; + for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]}); + return entries; + }, + size: function() { + var size = 0; + for (var property in this) if (property[0] === prefix) ++size; + return size; + }, + empty: function() { + for (var property in this) if (property[0] === prefix) return false; + return true; + }, + each: function(f) { + for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this); + } +}; + +function map$1(object, f) { + var map = new Map; + + // Copy constructor. + if (object instanceof Map) object.each(function(value, key) { map.set(key, value); }); + + // Index array by numeric index or specified key function. + else if (Array.isArray(object)) { + var i = -1, + n = object.length, + o; + + if (f == null) while (++i < n) map.set(i, object[i]); + else while (++i < n) map.set(f(o = object[i], i, object), o); + } + + // Convert object to map. + else if (object) for (var key in object) map.set(key, object[key]); + + return map; +} + +function nest() { + var keys = [], + sortKeys = [], + sortValues, + rollup, + nest; + + function apply(array, depth, createResult, setResult) { + if (depth >= keys.length) { + if (sortValues != null) array.sort(sortValues); + return rollup != null ? rollup(array) : array; + } + + var i = -1, + n = array.length, + key = keys[depth++], + keyValue, + value, + valuesByKey = map$1(), + values, + result = createResult(); + + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) { + values.push(value); + } else { + valuesByKey.set(keyValue, [value]); + } + } + + valuesByKey.each(function(values, key) { + setResult(result, key, apply(values, depth, createResult, setResult)); + }); + + return result; + } + + function entries(map, depth) { + if (++depth > keys.length) return map; + var array, sortKey = sortKeys[depth - 1]; + if (rollup != null && depth >= keys.length) array = map.entries(); + else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); }); + return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array; + } + + return nest = { + object: function(array) { return apply(array, 0, createObject, setObject); }, + map: function(array) { return apply(array, 0, createMap, setMap); }, + entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); }, + key: function(d) { keys.push(d); return nest; }, + sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; }, + sortValues: function(order) { sortValues = order; return nest; }, + rollup: function(f) { rollup = f; return nest; } + }; +} + +function createObject() { + return {}; +} + +function setObject(object, key, value) { + object[key] = value; +} + +function createMap() { + return map$1(); +} + +function setMap(map, key, value) { + map.set(key, value); +} + +function Set() {} + +var proto = map$1.prototype; + +Set.prototype = set$2.prototype = { + constructor: Set, + has: proto.has, + add: function(value) { + value += ""; + this[prefix + value] = value; + return this; + }, + remove: proto.remove, + clear: proto.clear, + values: proto.keys, + size: proto.size, + empty: proto.empty, + each: proto.each +}; + +function set$2(object, f) { + var set = new Set; + + // Copy constructor. + if (object instanceof Set) object.each(function(value) { set.add(value); }); + + // Otherwise, assume it’s an array. + else if (object) { + var i = -1, n = object.length; + if (f == null) while (++i < n) set.add(object[i]); + else while (++i < n) set.add(f(object[i], i, object)); + } + + return set; +} + +function keys(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; +} + +function values(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; +} + +function entries(map) { + var entries = []; + for (var key in map) entries.push({key: key, value: map[key]}); + return entries; +} + +var EOL = {}; +var EOF = {}; +var QUOTE = 34; +var NEWLINE = 10; +var RETURN = 13; + +function objectConverter(columns) { + return new Function("d", "return {" + columns.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); +} + +function customConverter(columns, f) { + var object = objectConverter(columns); + return function(row, i) { + return f(object(row), i, columns); + }; +} + +// Compute unique columns in order of discovery. +function inferColumns(rows) { + var columnSet = Object.create(null), + columns = []; + + rows.forEach(function(row) { + for (var column in row) { + if (!(column in columnSet)) { + columns.push(columnSet[column] = column); + } + } + }); + + return columns; +} + +function dsv(delimiter) { + var reFormat = new RegExp("[\"" + delimiter + "\n\r]"), + DELIMITER = delimiter.charCodeAt(0); + + function parse(text, f) { + var convert, columns, rows = parseRows(text, function(row, i) { + if (convert) return convert(row, i - 1); + columns = row, convert = f ? customConverter(row, f) : objectConverter(row); + }); + rows.columns = columns || []; + return rows; + } + + function parseRows(text, f) { + var rows = [], // output rows + N = text.length, + I = 0, // current character index + n = 0, // current line number + t, // current token + eof = N <= 0, // current token followed by EOF? + eol = false; // current token followed by EOL? + + // Strip the trailing newline. + if (text.charCodeAt(N - 1) === NEWLINE) --N; + if (text.charCodeAt(N - 1) === RETURN) --N; + + function token() { + if (eof) return EOF; + if (eol) return eol = false, EOL; + + // Unescape quotes. + var i, j = I, c; + if (text.charCodeAt(j) === QUOTE) { + while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE); + if ((i = I) >= N) eof = true; + else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true; + else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; } + return text.slice(j + 1, i - 1).replace(/""/g, "\""); + } + + // Find next delimiter or newline. + while (I < N) { + if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true; + else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; } + else if (c !== DELIMITER) continue; + return text.slice(j, i); + } + + // Return last token before EOF. + return eof = true, text.slice(j, N); + } + + while ((t = token()) !== EOF) { + var row = []; + while (t !== EOL && t !== EOF) row.push(t), t = token(); + if (f && (row = f(row, n++)) == null) continue; + rows.push(row); + } + + return rows; + } + + function format(rows, columns) { + if (columns == null) columns = inferColumns(rows); + return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) { + return columns.map(function(column) { + return formatValue(row[column]); + }).join(delimiter); + })).join("\n"); + } + + function formatRows(rows) { + return rows.map(formatRow).join("\n"); + } + + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + + function formatValue(text) { + return text == null ? "" + : reFormat.test(text += "") ? "\"" + text.replace(/"/g, "\"\"") + "\"" + : text; + } + + return { + parse: parse, + parseRows: parseRows, + format: format, + formatRows: formatRows + }; +} + +var csv = dsv(","); + +var csvParse = csv.parse; +var csvParseRows = csv.parseRows; +var csvFormat = csv.format; +var csvFormatRows = csv.formatRows; + +var tsv = dsv("\t"); + +var tsvParse = tsv.parse; +var tsvParseRows = tsv.parseRows; +var tsvFormat = tsv.format; +var tsvFormatRows = tsv.formatRows; + +function center$1(x, y) { + var nodes; + + if (x == null) x = 0; + if (y == null) y = 0; + + function force() { + var i, + n = nodes.length, + node, + sx = 0, + sy = 0; + + for (i = 0; i < n; ++i) { + node = nodes[i], sx += node.x, sy += node.y; + } + + for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) { + node = nodes[i], node.x -= sx, node.y -= sy; + } + } + + force.initialize = function(_) { + nodes = _; + }; + + force.x = function(_) { + return arguments.length ? (x = +_, force) : x; + }; + + force.y = function(_) { + return arguments.length ? (y = +_, force) : y; + }; + + return force; +} + +function constant$6(x) { + return function() { + return x; + }; +} + +function jiggle() { + return (Math.random() - 0.5) * 1e-6; +} + +function tree_add(d) { + var x = +this._x.call(null, d), + y = +this._y.call(null, d); + return add(this.cover(x, y), x, y, d); +} + +function add(tree, x, y, d) { + if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points + + var parent, + node = tree._root, + leaf = {data: d}, + x0 = tree._x0, + y0 = tree._y0, + x1 = tree._x1, + y1 = tree._y1, + xm, + ym, + xp, + yp, + right, + bottom, + i, + j; + + // If the tree is empty, initialize the root as a leaf. + if (!node) return tree._root = leaf, tree; + + // Find the existing leaf for the new point, or add it. + while (node.length) { + if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; + if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; + if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree; + } + + // Is the new point is exactly coincident with the existing point? + xp = +tree._x.call(null, node.data); + yp = +tree._y.call(null, node.data); + if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; + + // Otherwise, split the leaf node until the old and new point are separated. + do { + parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4); + if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; + if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; + } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm))); + return parent[j] = node, parent[i] = leaf, tree; +} + +function addAll(data) { + var d, i, n = data.length, + x, + y, + xz = new Array(n), + yz = new Array(n), + x0 = Infinity, + y0 = Infinity, + x1 = -Infinity, + y1 = -Infinity; + + // Compute the points and their extent. + for (i = 0; i < n; ++i) { + if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue; + xz[i] = x; + yz[i] = y; + if (x < x0) x0 = x; + if (x > x1) x1 = x; + if (y < y0) y0 = y; + if (y > y1) y1 = y; + } + + // If there were no (valid) points, inherit the existing extent. + if (x1 < x0) x0 = this._x0, x1 = this._x1; + if (y1 < y0) y0 = this._y0, y1 = this._y1; + + // Expand the tree to cover the new points. + this.cover(x0, y0).cover(x1, y1); + + // Add the new points. + for (i = 0; i < n; ++i) { + add(this, xz[i], yz[i], data[i]); + } + + return this; +} + +function tree_cover(x, y) { + if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points + + var x0 = this._x0, + y0 = this._y0, + x1 = this._x1, + y1 = this._y1; + + // If the quadtree has no extent, initialize them. + // Integer extent are necessary so that if we later double the extent, + // the existing quadrant boundaries don’t change due to floating point error! + if (isNaN(x0)) { + x1 = (x0 = Math.floor(x)) + 1; + y1 = (y0 = Math.floor(y)) + 1; + } + + // Otherwise, double repeatedly to cover. + else if (x0 > x || x > x1 || y0 > y || y > y1) { + var z = x1 - x0, + node = this._root, + parent, + i; + + switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) { + case 0: { + do parent = new Array(4), parent[i] = node, node = parent; + while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1); + break; + } + case 1: { + do parent = new Array(4), parent[i] = node, node = parent; + while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1); + break; + } + case 2: { + do parent = new Array(4), parent[i] = node, node = parent; + while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y); + break; + } + case 3: { + do parent = new Array(4), parent[i] = node, node = parent; + while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y); + break; + } + } + + if (this._root && this._root.length) this._root = node; + } + + // If the quadtree covers the point already, just return. + else return this; + + this._x0 = x0; + this._y0 = y0; + this._x1 = x1; + this._y1 = y1; + return this; +} + +function tree_data() { + var data = []; + this.visit(function(node) { + if (!node.length) do data.push(node.data); while (node = node.next) + }); + return data; +} + +function tree_extent(_) { + return arguments.length + ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1]) + : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]]; +} + +function Quad(node, x0, y0, x1, y1) { + this.node = node; + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; +} + +function tree_find(x, y, radius) { + var data, + x0 = this._x0, + y0 = this._y0, + x1, + y1, + x2, + y2, + x3 = this._x1, + y3 = this._y1, + quads = [], + node = this._root, + q, + i; + + if (node) quads.push(new Quad(node, x0, y0, x3, y3)); + if (radius == null) radius = Infinity; + else { + x0 = x - radius, y0 = y - radius; + x3 = x + radius, y3 = y + radius; + radius *= radius; + } + + while (q = quads.pop()) { + + // Stop searching if this quadrant can’t contain a closer node. + if (!(node = q.node) + || (x1 = q.x0) > x3 + || (y1 = q.y0) > y3 + || (x2 = q.x1) < x0 + || (y2 = q.y1) < y0) continue; + + // Bisect the current quadrant. + if (node.length) { + var xm = (x1 + x2) / 2, + ym = (y1 + y2) / 2; + + quads.push( + new Quad(node[3], xm, ym, x2, y2), + new Quad(node[2], x1, ym, xm, y2), + new Quad(node[1], xm, y1, x2, ym), + new Quad(node[0], x1, y1, xm, ym) + ); + + // Visit the closest quadrant first. + if (i = (y >= ym) << 1 | (x >= xm)) { + q = quads[quads.length - 1]; + quads[quads.length - 1] = quads[quads.length - 1 - i]; + quads[quads.length - 1 - i] = q; + } + } + + // Visit this point. (Visiting coincident points isn’t necessary!) + else { + var dx = x - +this._x.call(null, node.data), + dy = y - +this._y.call(null, node.data), + d2 = dx * dx + dy * dy; + if (d2 < radius) { + var d = Math.sqrt(radius = d2); + x0 = x - d, y0 = y - d; + x3 = x + d, y3 = y + d; + data = node.data; + } + } + } + + return data; +} + +function tree_remove(d) { + if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points + + var parent, + node = this._root, + retainer, + previous, + next, + x0 = this._x0, + y0 = this._y0, + x1 = this._x1, + y1 = this._y1, + x, + y, + xm, + ym, + right, + bottom, + i, + j; + + // If the tree is empty, initialize the root as a leaf. + if (!node) return this; + + // Find the leaf node for the point. + // While descending, also retain the deepest parent with a non-removed sibling. + if (node.length) while (true) { + if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; + if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; + if (!(parent = node, node = node[i = bottom << 1 | right])) return this; + if (!node.length) break; + if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i; + } + + // Find the point to remove. + while (node.data !== d) if (!(previous = node, node = node.next)) return this; + if (next = node.next) delete node.next; + + // If there are multiple coincident points, remove just the point. + if (previous) return next ? previous.next = next : delete previous.next, this; + + // If this is the root point, remove it. + if (!parent) return this._root = next, this; + + // Remove this leaf. + next ? parent[i] = next : delete parent[i]; + + // If the parent now contains exactly one leaf, collapse superfluous parents. + if ((node = parent[0] || parent[1] || parent[2] || parent[3]) + && node === (parent[3] || parent[2] || parent[1] || parent[0]) + && !node.length) { + if (retainer) retainer[j] = node; + else this._root = node; + } + + return this; +} + +function removeAll(data) { + for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]); + return this; +} + +function tree_root() { + return this._root; +} + +function tree_size() { + var size = 0; + this.visit(function(node) { + if (!node.length) do ++size; while (node = node.next) + }); + return size; +} + +function tree_visit(callback) { + var quads = [], q, node = this._root, child, x0, y0, x1, y1; + if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1)); + while (q = quads.pop()) { + if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) { + var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; + if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); + if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); + if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); + if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); + } + } + return this; +} + +function tree_visitAfter(callback) { + var quads = [], next = [], q; + if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1)); + while (q = quads.pop()) { + var node = q.node; + if (node.length) { + var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; + if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); + if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); + if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); + if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); + } + next.push(q); + } + while (q = next.pop()) { + callback(q.node, q.x0, q.y0, q.x1, q.y1); + } + return this; +} + +function defaultX(d) { + return d[0]; +} + +function tree_x(_) { + return arguments.length ? (this._x = _, this) : this._x; +} + +function defaultY(d) { + return d[1]; +} + +function tree_y(_) { + return arguments.length ? (this._y = _, this) : this._y; +} + +function quadtree(nodes, x, y) { + var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN); + return nodes == null ? tree : tree.addAll(nodes); +} + +function Quadtree(x, y, x0, y0, x1, y1) { + this._x = x; + this._y = y; + this._x0 = x0; + this._y0 = y0; + this._x1 = x1; + this._y1 = y1; + this._root = undefined; +} + +function leaf_copy(leaf) { + var copy = {data: leaf.data}, next = copy; + while (leaf = leaf.next) next = next.next = {data: leaf.data}; + return copy; +} + +var treeProto = quadtree.prototype = Quadtree.prototype; + +treeProto.copy = function() { + var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1), + node = this._root, + nodes, + child; + + if (!node) return copy; + + if (!node.length) return copy._root = leaf_copy(node), copy; + + nodes = [{source: node, target: copy._root = new Array(4)}]; + while (node = nodes.pop()) { + for (var i = 0; i < 4; ++i) { + if (child = node.source[i]) { + if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)}); + else node.target[i] = leaf_copy(child); + } + } + } + + return copy; +}; + +treeProto.add = tree_add; +treeProto.addAll = addAll; +treeProto.cover = tree_cover; +treeProto.data = tree_data; +treeProto.extent = tree_extent; +treeProto.find = tree_find; +treeProto.remove = tree_remove; +treeProto.removeAll = removeAll; +treeProto.root = tree_root; +treeProto.size = tree_size; +treeProto.visit = tree_visit; +treeProto.visitAfter = tree_visitAfter; +treeProto.x = tree_x; +treeProto.y = tree_y; + +function x(d) { + return d.x + d.vx; +} + +function y(d) { + return d.y + d.vy; +} + +function collide(radius) { + var nodes, + radii, + strength = 1, + iterations = 1; + + if (typeof radius !== "function") radius = constant$6(radius == null ? 1 : +radius); + + function force() { + var i, n = nodes.length, + tree, + node, + xi, + yi, + ri, + ri2; + + for (var k = 0; k < iterations; ++k) { + tree = quadtree(nodes, x, y).visitAfter(prepare); + for (i = 0; i < n; ++i) { + node = nodes[i]; + ri = radii[node.index], ri2 = ri * ri; + xi = node.x + node.vx; + yi = node.y + node.vy; + tree.visit(apply); + } + } + + function apply(quad, x0, y0, x1, y1) { + var data = quad.data, rj = quad.r, r = ri + rj; + if (data) { + if (data.index > node.index) { + var x = xi - data.x - data.vx, + y = yi - data.y - data.vy, + l = x * x + y * y; + if (l < r * r) { + if (x === 0) x = jiggle(), l += x * x; + if (y === 0) y = jiggle(), l += y * y; + l = (r - (l = Math.sqrt(l))) / l * strength; + node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj)); + node.vy += (y *= l) * r; + data.vx -= x * (r = 1 - r); + data.vy -= y * r; + } + } + return; + } + return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r; + } + } + + function prepare(quad) { + if (quad.data) return quad.r = radii[quad.data.index]; + for (var i = quad.r = 0; i < 4; ++i) { + if (quad[i] && quad[i].r > quad.r) { + quad.r = quad[i].r; + } + } + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length, node; + radii = new Array(n); + for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes); + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.iterations = function(_) { + return arguments.length ? (iterations = +_, force) : iterations; + }; + + force.strength = function(_) { + return arguments.length ? (strength = +_, force) : strength; + }; + + force.radius = function(_) { + return arguments.length ? (radius = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : radius; + }; + + return force; +} + +function index(d) { + return d.index; +} + +function find(nodeById, nodeId) { + var node = nodeById.get(nodeId); + if (!node) throw new Error("missing: " + nodeId); + return node; +} + +function link(links) { + var id = index, + strength = defaultStrength, + strengths, + distance = constant$6(30), + distances, + nodes, + count, + bias, + iterations = 1; + + if (links == null) links = []; + + function defaultStrength(link) { + return 1 / Math.min(count[link.source.index], count[link.target.index]); + } + + function force(alpha) { + for (var k = 0, n = links.length; k < iterations; ++k) { + for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) { + link = links[i], source = link.source, target = link.target; + x = target.x + target.vx - source.x - source.vx || jiggle(); + y = target.y + target.vy - source.y - source.vy || jiggle(); + l = Math.sqrt(x * x + y * y); + l = (l - distances[i]) / l * alpha * strengths[i]; + x *= l, y *= l; + target.vx -= x * (b = bias[i]); + target.vy -= y * b; + source.vx += x * (b = 1 - b); + source.vy += y * b; + } + } + } + + function initialize() { + if (!nodes) return; + + var i, + n = nodes.length, + m = links.length, + nodeById = map$1(nodes, id), + link; + + for (i = 0, count = new Array(n); i < m; ++i) { + link = links[i], link.index = i; + if (typeof link.source !== "object") link.source = find(nodeById, link.source); + if (typeof link.target !== "object") link.target = find(nodeById, link.target); + count[link.source.index] = (count[link.source.index] || 0) + 1; + count[link.target.index] = (count[link.target.index] || 0) + 1; + } + + for (i = 0, bias = new Array(m); i < m; ++i) { + link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]); + } + + strengths = new Array(m), initializeStrength(); + distances = new Array(m), initializeDistance(); + } + + function initializeStrength() { + if (!nodes) return; + + for (var i = 0, n = links.length; i < n; ++i) { + strengths[i] = +strength(links[i], i, links); + } + } + + function initializeDistance() { + if (!nodes) return; + + for (var i = 0, n = links.length; i < n; ++i) { + distances[i] = +distance(links[i], i, links); + } + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.links = function(_) { + return arguments.length ? (links = _, initialize(), force) : links; + }; + + force.id = function(_) { + return arguments.length ? (id = _, force) : id; + }; + + force.iterations = function(_) { + return arguments.length ? (iterations = +_, force) : iterations; + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initializeStrength(), force) : strength; + }; + + force.distance = function(_) { + return arguments.length ? (distance = typeof _ === "function" ? _ : constant$6(+_), initializeDistance(), force) : distance; + }; + + return force; +} + +function x$1(d) { + return d.x; +} + +function y$1(d) { + return d.y; +} + +var initialRadius = 10; +var initialAngle = Math.PI * (3 - Math.sqrt(5)); + +function simulation(nodes) { + var simulation, + alpha = 1, + alphaMin = 0.001, + alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), + alphaTarget = 0, + velocityDecay = 0.6, + forces = map$1(), + stepper = timer(step), + event = dispatch("tick", "end"); + + if (nodes == null) nodes = []; + + function step() { + tick(); + event.call("tick", simulation); + if (alpha < alphaMin) { + stepper.stop(); + event.call("end", simulation); + } + } + + function tick() { + var i, n = nodes.length, node; + + alpha += (alphaTarget - alpha) * alphaDecay; + + forces.each(function(force) { + force(alpha); + }); + + for (i = 0; i < n; ++i) { + node = nodes[i]; + if (node.fx == null) node.x += node.vx *= velocityDecay; + else node.x = node.fx, node.vx = 0; + if (node.fy == null) node.y += node.vy *= velocityDecay; + else node.y = node.fy, node.vy = 0; + } + } + + function initializeNodes() { + for (var i = 0, n = nodes.length, node; i < n; ++i) { + node = nodes[i], node.index = i; + if (isNaN(node.x) || isNaN(node.y)) { + var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle; + node.x = radius * Math.cos(angle); + node.y = radius * Math.sin(angle); + } + if (isNaN(node.vx) || isNaN(node.vy)) { + node.vx = node.vy = 0; + } + } + } + + function initializeForce(force) { + if (force.initialize) force.initialize(nodes); + return force; + } + + initializeNodes(); + + return simulation = { + tick: tick, + + restart: function() { + return stepper.restart(step), simulation; + }, + + stop: function() { + return stepper.stop(), simulation; + }, + + nodes: function(_) { + return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes; + }, + + alpha: function(_) { + return arguments.length ? (alpha = +_, simulation) : alpha; + }, + + alphaMin: function(_) { + return arguments.length ? (alphaMin = +_, simulation) : alphaMin; + }, + + alphaDecay: function(_) { + return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay; + }, + + alphaTarget: function(_) { + return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget; + }, + + velocityDecay: function(_) { + return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay; + }, + + force: function(name, _) { + return arguments.length > 1 ? (_ == null ? forces.remove(name) : forces.set(name, initializeForce(_)), simulation) : forces.get(name); + }, + + find: function(x, y, radius) { + var i = 0, + n = nodes.length, + dx, + dy, + d2, + node, + closest; + + if (radius == null) radius = Infinity; + else radius *= radius; + + for (i = 0; i < n; ++i) { + node = nodes[i]; + dx = x - node.x; + dy = y - node.y; + d2 = dx * dx + dy * dy; + if (d2 < radius) closest = node, radius = d2; + } + + return closest; + }, + + on: function(name, _) { + return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name); + } + }; +} + +function manyBody() { + var nodes, + node, + alpha, + strength = constant$6(-30), + strengths, + distanceMin2 = 1, + distanceMax2 = Infinity, + theta2 = 0.81; + + function force(_) { + var i, n = nodes.length, tree = quadtree(nodes, x$1, y$1).visitAfter(accumulate); + for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply); + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length, node; + strengths = new Array(n); + for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes); + } + + function accumulate(quad) { + var strength = 0, q, c, weight = 0, x, y, i; + + // For internal nodes, accumulate forces from child quadrants. + if (quad.length) { + for (x = y = i = 0; i < 4; ++i) { + if ((q = quad[i]) && (c = Math.abs(q.value))) { + strength += q.value, weight += c, x += c * q.x, y += c * q.y; + } + } + quad.x = x / weight; + quad.y = y / weight; + } + + // For leaf nodes, accumulate forces from coincident quadrants. + else { + q = quad; + q.x = q.data.x; + q.y = q.data.y; + do strength += strengths[q.data.index]; + while (q = q.next); + } + + quad.value = strength; + } + + function apply(quad, x1, _, x2) { + if (!quad.value) return true; + + var x = quad.x - node.x, + y = quad.y - node.y, + w = x2 - x1, + l = x * x + y * y; + + // Apply the Barnes-Hut approximation if possible. + // Limit forces for very close nodes; randomize direction if coincident. + if (w * w / theta2 < l) { + if (l < distanceMax2) { + if (x === 0) x = jiggle(), l += x * x; + if (y === 0) y = jiggle(), l += y * y; + if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); + node.vx += x * quad.value * alpha / l; + node.vy += y * quad.value * alpha / l; + } + return true; + } + + // Otherwise, process points directly. + else if (quad.length || l >= distanceMax2) return; + + // Limit forces for very close nodes; randomize direction if coincident. + if (quad.data !== node || quad.next) { + if (x === 0) x = jiggle(), l += x * x; + if (y === 0) y = jiggle(), l += y * y; + if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); + } + + do if (quad.data !== node) { + w = strengths[quad.data.index] * alpha / l; + node.vx += x * w; + node.vy += y * w; + } while (quad = quad.next); + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : strength; + }; + + force.distanceMin = function(_) { + return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2); + }; + + force.distanceMax = function(_) { + return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2); + }; + + force.theta = function(_) { + return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2); + }; + + return force; +} + +function radial(radius, x, y) { + var nodes, + strength = constant$6(0.1), + strengths, + radiuses; + + if (typeof radius !== "function") radius = constant$6(+radius); + if (x == null) x = 0; + if (y == null) y = 0; + + function force(alpha) { + for (var i = 0, n = nodes.length; i < n; ++i) { + var node = nodes[i], + dx = node.x - x || 1e-6, + dy = node.y - y || 1e-6, + r = Math.sqrt(dx * dx + dy * dy), + k = (radiuses[i] - r) * strengths[i] * alpha / r; + node.vx += dx * k; + node.vy += dy * k; + } + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length; + strengths = new Array(n); + radiuses = new Array(n); + for (i = 0; i < n; ++i) { + radiuses[i] = +radius(nodes[i], i, nodes); + strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes); + } + } + + force.initialize = function(_) { + nodes = _, initialize(); + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : strength; + }; + + force.radius = function(_) { + return arguments.length ? (radius = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : radius; + }; + + force.x = function(_) { + return arguments.length ? (x = +_, force) : x; + }; + + force.y = function(_) { + return arguments.length ? (y = +_, force) : y; + }; + + return force; +} + +function x$2(x) { + var strength = constant$6(0.1), + nodes, + strengths, + xz; + + if (typeof x !== "function") x = constant$6(x == null ? 0 : +x); + + function force(alpha) { + for (var i = 0, n = nodes.length, node; i < n; ++i) { + node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha; + } + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length; + strengths = new Array(n); + xz = new Array(n); + for (i = 0; i < n; ++i) { + strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes); + } + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : strength; + }; + + force.x = function(_) { + return arguments.length ? (x = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : x; + }; + + return force; +} + +function y$2(y) { + var strength = constant$6(0.1), + nodes, + strengths, + yz; + + if (typeof y !== "function") y = constant$6(y == null ? 0 : +y); + + function force(alpha) { + for (var i = 0, n = nodes.length, node; i < n; ++i) { + node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha; + } + } + + function initialize() { + if (!nodes) return; + var i, n = nodes.length; + strengths = new Array(n); + yz = new Array(n); + for (i = 0; i < n; ++i) { + strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes); + } + } + + force.initialize = function(_) { + nodes = _; + initialize(); + }; + + force.strength = function(_) { + return arguments.length ? (strength = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : strength; + }; + + force.y = function(_) { + return arguments.length ? (y = typeof _ === "function" ? _ : constant$6(+_), initialize(), force) : y; + }; + + return force; +} + +// Computes the decimal coefficient and exponent of the specified number x with +// significant digits p, where x is positive and p is in [1, 21] or undefined. +// For example, formatDecimal(1.23) returns ["123", 0]. +function formatDecimal(x, p) { + if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity + var i, coefficient = x.slice(0, i); + + // The string returned by toExponential either has the form \d\.\d+e[-+]\d+ + // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3). + return [ + coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, + +x.slice(i + 1) + ]; +} + +function exponent$1(x) { + return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN; +} + +function formatGroup(grouping, thousands) { + return function(value, width) { + var i = value.length, + t = [], + j = 0, + g = grouping[0], + length = 0; + + while (i > 0 && g > 0) { + if (length + g + 1 > width) g = Math.max(1, width - length); + t.push(value.substring(i -= g, i + g)); + if ((length += g + 1) > width) break; + g = grouping[j = (j + 1) % grouping.length]; + } + + return t.reverse().join(thousands); + }; +} + +function formatNumerals(numerals) { + return function(value) { + return value.replace(/[0-9]/g, function(i) { + return numerals[+i]; + }); + }; +} + +function formatDefault(x, p) { + x = x.toPrecision(p); + + out: for (var n = x.length, i = 1, i0 = -1, i1; i < n; ++i) { + switch (x[i]) { + case ".": i0 = i1 = i; break; + case "0": if (i0 === 0) i0 = i; i1 = i; break; + case "e": break out; + default: if (i0 > 0) i0 = 0; break; + } + } + + return i0 > 0 ? x.slice(0, i0) + x.slice(i1 + 1) : x; +} + +var prefixExponent; + +function formatPrefixAuto(x, p) { + var d = formatDecimal(x, p); + if (!d) return x + ""; + var coefficient = d[0], + exponent = d[1], + i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, + n = coefficient.length; + return i === n ? coefficient + : i > n ? coefficient + new Array(i - n + 1).join("0") + : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i) + : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y! +} + +function formatRounded(x, p) { + var d = formatDecimal(x, p); + if (!d) return x + ""; + var coefficient = d[0], + exponent = d[1]; + return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient + : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1) + : coefficient + new Array(exponent - coefficient.length + 2).join("0"); +} + +var formatTypes = { + "": formatDefault, + "%": function(x, p) { return (x * 100).toFixed(p); }, + "b": function(x) { return Math.round(x).toString(2); }, + "c": function(x) { return x + ""; }, + "d": function(x) { return Math.round(x).toString(10); }, + "e": function(x, p) { return x.toExponential(p); }, + "f": function(x, p) { return x.toFixed(p); }, + "g": function(x, p) { return x.toPrecision(p); }, + "o": function(x) { return Math.round(x).toString(8); }, + "p": function(x, p) { return formatRounded(x * 100, p); }, + "r": formatRounded, + "s": formatPrefixAuto, + "X": function(x) { return Math.round(x).toString(16).toUpperCase(); }, + "x": function(x) { return Math.round(x).toString(16); } +}; + +// [[fill]align][sign][symbol][0][width][,][.precision][type] +var re = /^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i; + +function formatSpecifier(specifier) { + return new FormatSpecifier(specifier); +} + +formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof + +function FormatSpecifier(specifier) { + if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier); + + var match, + fill = match[1] || " ", + align = match[2] || ">", + sign = match[3] || "-", + symbol = match[4] || "", + zero = !!match[5], + width = match[6] && +match[6], + comma = !!match[7], + precision = match[8] && +match[8].slice(1), + type = match[9] || ""; + + // The "n" type is an alias for ",g". + if (type === "n") comma = true, type = "g"; + + // Map invalid types to the default format. + else if (!formatTypes[type]) type = ""; + + // If zero fill is specified, padding goes after sign and before digits. + if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "="; + + this.fill = fill; + this.align = align; + this.sign = sign; + this.symbol = symbol; + this.zero = zero; + this.width = width; + this.comma = comma; + this.precision = precision; + this.type = type; +} + +FormatSpecifier.prototype.toString = function() { + return this.fill + + this.align + + this.sign + + this.symbol + + (this.zero ? "0" : "") + + (this.width == null ? "" : Math.max(1, this.width | 0)) + + (this.comma ? "," : "") + + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0)) + + this.type; +}; + +function identity$3(x) { + return x; +} + +var prefixes = ["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"]; + +function formatLocale(locale) { + var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$3, + currency = locale.currency, + decimal = locale.decimal, + numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$3, + percent = locale.percent || "%"; + + function newFormat(specifier) { + specifier = formatSpecifier(specifier); + + var fill = specifier.fill, + align = specifier.align, + sign = specifier.sign, + symbol = specifier.symbol, + zero = specifier.zero, + width = specifier.width, + comma = specifier.comma, + precision = specifier.precision, + type = specifier.type; + + // Compute the prefix and suffix. + // For SI-prefix, the suffix is lazily computed. + var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", + suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : ""; + + // What format function should we use? + // Is this an integer type? + // Can this type generate exponential notation? + var formatType = formatTypes[type], + maybeSuffix = !type || /[defgprs%]/.test(type); + + // Set the default precision if not specified, + // or clamp the specified precision to the supported range. + // For significant precision, it must be in [1, 21]. + // For fixed precision, it must be in [0, 20]. + precision = precision == null ? (type ? 6 : 12) + : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) + : Math.max(0, Math.min(20, precision)); + + function format(value) { + var valuePrefix = prefix, + valueSuffix = suffix, + i, n, c; + + if (type === "c") { + valueSuffix = formatType(value) + valueSuffix; + value = ""; + } else { + value = +value; + + // Perform the initial formatting. + var valueNegative = value < 0; + value = formatType(Math.abs(value), precision); + + // If a negative value rounds to zero during formatting, treat as positive. + if (valueNegative && +value === 0) valueNegative = false; + + // Compute the prefix and suffix. + valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; + valueSuffix = valueSuffix + (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + (valueNegative && sign === "(" ? ")" : ""); + + // Break the formatted value into the integer “value” part that can be + // grouped, and fractional or exponential “suffix” part that is not. + if (maybeSuffix) { + i = -1, n = value.length; + while (++i < n) { + if (c = value.charCodeAt(i), 48 > c || c > 57) { + valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix; + value = value.slice(0, i); + break; + } + } + } + } + + // If the fill character is not "0", grouping is applied before padding. + if (comma && !zero) value = group(value, Infinity); + + // Compute the padding. + var length = valuePrefix.length + value.length + valueSuffix.length, + padding = length < width ? new Array(width - length + 1).join(fill) : ""; + + // If the fill character is "0", grouping is applied after padding. + if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; + + // Reconstruct the final output based on the desired alignment. + switch (align) { + case "<": value = valuePrefix + value + valueSuffix + padding; break; + case "=": value = valuePrefix + padding + value + valueSuffix; break; + case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break; + default: value = padding + valuePrefix + value + valueSuffix; break; + } + + return numerals(value); + } + + format.toString = function() { + return specifier + ""; + }; + + return format; + } + + function formatPrefix(specifier, value) { + var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), + e = Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3, + k = Math.pow(10, -e), + prefix = prefixes[8 + e / 3]; + return function(value) { + return f(k * value) + prefix; + }; + } + + return { + format: newFormat, + formatPrefix: formatPrefix + }; +} + +var locale; + + + +defaultLocale({ + decimal: ".", + thousands: ",", + grouping: [3], + currency: ["$", ""] +}); + +function defaultLocale(definition) { + locale = formatLocale(definition); + exports.format = locale.format; + exports.formatPrefix = locale.formatPrefix; + return locale; +} + +function precisionFixed(step) { + return Math.max(0, -exponent$1(Math.abs(step))); +} + +function precisionPrefix(step, value) { + return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3 - exponent$1(Math.abs(step))); +} + +function precisionRound(step, max) { + step = Math.abs(step), max = Math.abs(max) - step; + return Math.max(0, exponent$1(max) - exponent$1(step)) + 1; +} + +// Adds floating point numbers with twice the normal precision. +// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and +// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) +// 305–363 (1997). +// Code adapted from GeographicLib by Charles F. F. Karney, +// http://geographiclib.sourceforge.net/ + +function adder() { + return new Adder; +} + +function Adder() { + this.reset(); +} + +Adder.prototype = { + constructor: Adder, + reset: function() { + this.s = // rounded value + this.t = 0; // exact error + }, + add: function(y) { + add$1(temp, y, this.t); + add$1(this, temp.s, this.s); + if (this.s) this.t += temp.t; + else this.s = temp.t; + }, + valueOf: function() { + return this.s; + } +}; + +var temp = new Adder; + +function add$1(adder, a, b) { + var x = adder.s = a + b, + bv = x - a, + av = x - bv; + adder.t = (a - av) + (b - bv); +} + +var epsilon$2 = 1e-6; +var epsilon2$1 = 1e-12; +var pi$3 = Math.PI; +var halfPi$2 = pi$3 / 2; +var quarterPi = pi$3 / 4; +var tau$3 = pi$3 * 2; + +var degrees$1 = 180 / pi$3; +var radians = pi$3 / 180; + +var abs = Math.abs; +var atan = Math.atan; +var atan2 = Math.atan2; +var cos$1 = Math.cos; +var ceil = Math.ceil; +var exp = Math.exp; + +var log = Math.log; +var pow = Math.pow; +var sin$1 = Math.sin; +var sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }; +var sqrt = Math.sqrt; +var tan = Math.tan; + +function acos(x) { + return x > 1 ? 0 : x < -1 ? pi$3 : Math.acos(x); +} + +function asin(x) { + return x > 1 ? halfPi$2 : x < -1 ? -halfPi$2 : Math.asin(x); +} + +function haversin(x) { + return (x = sin$1(x / 2)) * x; +} + +function noop$1() {} + +function streamGeometry(geometry, stream) { + if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) { + streamGeometryType[geometry.type](geometry, stream); + } +} + +var streamObjectType = { + Feature: function(object, stream) { + streamGeometry(object.geometry, stream); + }, + FeatureCollection: function(object, stream) { + var features = object.features, i = -1, n = features.length; + while (++i < n) streamGeometry(features[i].geometry, stream); + } +}; + +var streamGeometryType = { + Sphere: function(object, stream) { + stream.sphere(); + }, + Point: function(object, stream) { + object = object.coordinates; + stream.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, stream) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]); + }, + LineString: function(object, stream) { + streamLine(object.coordinates, stream, 0); + }, + MultiLineString: function(object, stream) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) streamLine(coordinates[i], stream, 0); + }, + Polygon: function(object, stream) { + streamPolygon(object.coordinates, stream); + }, + MultiPolygon: function(object, stream) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) streamPolygon(coordinates[i], stream); + }, + GeometryCollection: function(object, stream) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) streamGeometry(geometries[i], stream); + } +}; + +function streamLine(coordinates, stream, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + stream.lineStart(); + while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]); + stream.lineEnd(); +} + +function streamPolygon(coordinates, stream) { + var i = -1, n = coordinates.length; + stream.polygonStart(); + while (++i < n) streamLine(coordinates[i], stream, 1); + stream.polygonEnd(); +} + +function geoStream(object, stream) { + if (object && streamObjectType.hasOwnProperty(object.type)) { + streamObjectType[object.type](object, stream); + } else { + streamGeometry(object, stream); + } +} + +var areaRingSum = adder(); + +var areaSum = adder(); +var lambda00; +var phi00; +var lambda0; +var cosPhi0; +var sinPhi0; + +var areaStream = { + point: noop$1, + lineStart: noop$1, + lineEnd: noop$1, + polygonStart: function() { + areaRingSum.reset(); + areaStream.lineStart = areaRingStart; + areaStream.lineEnd = areaRingEnd; + }, + polygonEnd: function() { + var areaRing = +areaRingSum; + areaSum.add(areaRing < 0 ? tau$3 + areaRing : areaRing); + this.lineStart = this.lineEnd = this.point = noop$1; + }, + sphere: function() { + areaSum.add(tau$3); + } +}; + +function areaRingStart() { + areaStream.point = areaPointFirst; +} + +function areaRingEnd() { + areaPoint(lambda00, phi00); +} + +function areaPointFirst(lambda, phi) { + areaStream.point = areaPoint; + lambda00 = lambda, phi00 = phi; + lambda *= radians, phi *= radians; + lambda0 = lambda, cosPhi0 = cos$1(phi = phi / 2 + quarterPi), sinPhi0 = sin$1(phi); +} + +function areaPoint(lambda, phi) { + lambda *= radians, phi *= radians; + phi = phi / 2 + quarterPi; // half the angular distance from south pole + + // Spherical excess E for a spherical triangle with vertices: south pole, + // previous point, current point. Uses a formula derived from Cagnoli’s + // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2). + var dLambda = lambda - lambda0, + sdLambda = dLambda >= 0 ? 1 : -1, + adLambda = sdLambda * dLambda, + cosPhi = cos$1(phi), + sinPhi = sin$1(phi), + k = sinPhi0 * sinPhi, + u = cosPhi0 * cosPhi + k * cos$1(adLambda), + v = k * sdLambda * sin$1(adLambda); + areaRingSum.add(atan2(v, u)); + + // Advance the previous points. + lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi; +} + +function area(object) { + areaSum.reset(); + geoStream(object, areaStream); + return areaSum * 2; +} + +function spherical(cartesian) { + return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])]; +} + +function cartesian(spherical) { + var lambda = spherical[0], phi = spherical[1], cosPhi = cos$1(phi); + return [cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)]; +} + +function cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +function cartesianCross(a, b) { + return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; +} + +// TODO return a +function cartesianAddInPlace(a, b) { + a[0] += b[0], a[1] += b[1], a[2] += b[2]; +} + +function cartesianScale(vector, k) { + return [vector[0] * k, vector[1] * k, vector[2] * k]; +} + +// TODO return d +function cartesianNormalizeInPlace(d) { + var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l, d[1] /= l, d[2] /= l; +} + +var lambda0$1; +var phi0; +var lambda1; +var phi1; +var lambda2; +var lambda00$1; +var phi00$1; +var p0; +var deltaSum = adder(); +var ranges; +var range; + +var boundsStream = { + point: boundsPoint, + lineStart: boundsLineStart, + lineEnd: boundsLineEnd, + polygonStart: function() { + boundsStream.point = boundsRingPoint; + boundsStream.lineStart = boundsRingStart; + boundsStream.lineEnd = boundsRingEnd; + deltaSum.reset(); + areaStream.polygonStart(); + }, + polygonEnd: function() { + areaStream.polygonEnd(); + boundsStream.point = boundsPoint; + boundsStream.lineStart = boundsLineStart; + boundsStream.lineEnd = boundsLineEnd; + if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90); + else if (deltaSum > epsilon$2) phi1 = 90; + else if (deltaSum < -epsilon$2) phi0 = -90; + range[0] = lambda0$1, range[1] = lambda1; + } +}; + +function boundsPoint(lambda, phi) { + ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]); + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; +} + +function linePoint(lambda, phi) { + var p = cartesian([lambda * radians, phi * radians]); + if (p0) { + var normal = cartesianCross(p0, p), + equatorial = [normal[1], -normal[0], 0], + inflection = cartesianCross(equatorial, normal); + cartesianNormalizeInPlace(inflection); + inflection = spherical(inflection); + var delta = lambda - lambda2, + sign$$1 = delta > 0 ? 1 : -1, + lambdai = inflection[0] * degrees$1 * sign$$1, + phii, + antimeridian = abs(delta) > 180; + if (antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) { + phii = inflection[1] * degrees$1; + if (phii > phi1) phi1 = phii; + } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) { + phii = -inflection[1] * degrees$1; + if (phii < phi0) phi0 = phii; + } else { + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; + } + if (antimeridian) { + if (lambda < lambda2) { + if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; + } else { + if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; + } + } else { + if (lambda1 >= lambda0$1) { + if (lambda < lambda0$1) lambda0$1 = lambda; + if (lambda > lambda1) lambda1 = lambda; + } else { + if (lambda > lambda2) { + if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda; + } else { + if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda; + } + } + } + } else { + ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]); + } + if (phi < phi0) phi0 = phi; + if (phi > phi1) phi1 = phi; + p0 = p, lambda2 = lambda; +} + +function boundsLineStart() { + boundsStream.point = linePoint; +} + +function boundsLineEnd() { + range[0] = lambda0$1, range[1] = lambda1; + boundsStream.point = boundsPoint; + p0 = null; +} + +function boundsRingPoint(lambda, phi) { + if (p0) { + var delta = lambda - lambda2; + deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta); + } else { + lambda00$1 = lambda, phi00$1 = phi; + } + areaStream.point(lambda, phi); + linePoint(lambda, phi); +} + +function boundsRingStart() { + areaStream.lineStart(); +} + +function boundsRingEnd() { + boundsRingPoint(lambda00$1, phi00$1); + areaStream.lineEnd(); + if (abs(deltaSum) > epsilon$2) lambda0$1 = -(lambda1 = 180); + range[0] = lambda0$1, range[1] = lambda1; + p0 = null; +} + +// Finds the left-right distance between two longitudes. +// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want +// the distance between ±180° to be 360°. +function angle(lambda0, lambda1) { + return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1; +} + +function rangeCompare(a, b) { + return a[0] - b[0]; +} + +function rangeContains(range, x) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; +} + +function bounds(feature) { + var i, n, a, b, merged, deltaMax, delta; + + phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity); + ranges = []; + geoStream(feature, boundsStream); + + // First, sort ranges by their minimum longitudes. + if (n = ranges.length) { + ranges.sort(rangeCompare); + + // Then, merge any ranges that overlap. + for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) { + b = ranges[i]; + if (rangeContains(a, b[0]) || rangeContains(a, b[1])) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } + + // Finally, find the largest gap between the merged ranges. + // The final bounding box will be the inverse of this gap. + for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) { + b = merged[i]; + if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1]; + } + } + + ranges = range = null; + + return lambda0$1 === Infinity || phi0 === Infinity + ? [[NaN, NaN], [NaN, NaN]] + : [[lambda0$1, phi0], [lambda1, phi1]]; +} + +var W0; +var W1; +var X0; +var Y0; +var Z0; +var X1; +var Y1; +var Z1; +var X2; +var Y2; +var Z2; +var lambda00$2; +var phi00$2; +var x0; +var y0; +var z0; // previous point + +var centroidStream = { + sphere: noop$1, + point: centroidPoint, + lineStart: centroidLineStart, + lineEnd: centroidLineEnd, + polygonStart: function() { + centroidStream.lineStart = centroidRingStart; + centroidStream.lineEnd = centroidRingEnd; + }, + polygonEnd: function() { + centroidStream.lineStart = centroidLineStart; + centroidStream.lineEnd = centroidLineEnd; + } +}; + +// Arithmetic mean of Cartesian vectors. +function centroidPoint(lambda, phi) { + lambda *= radians, phi *= radians; + var cosPhi = cos$1(phi); + centroidPointCartesian(cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)); +} + +function centroidPointCartesian(x, y, z) { + ++W0; + X0 += (x - X0) / W0; + Y0 += (y - Y0) / W0; + Z0 += (z - Z0) / W0; +} + +function centroidLineStart() { + centroidStream.point = centroidLinePointFirst; +} + +function centroidLinePointFirst(lambda, phi) { + lambda *= radians, phi *= radians; + var cosPhi = cos$1(phi); + x0 = cosPhi * cos$1(lambda); + y0 = cosPhi * sin$1(lambda); + z0 = sin$1(phi); + centroidStream.point = centroidLinePoint; + centroidPointCartesian(x0, y0, z0); +} + +function centroidLinePoint(lambda, phi) { + lambda *= radians, phi *= radians; + var cosPhi = cos$1(phi), + x = cosPhi * cos$1(lambda), + y = cosPhi * sin$1(lambda), + z = sin$1(phi), + w = atan2(sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + W1 += w; + X1 += w * (x0 + (x0 = x)); + Y1 += w * (y0 + (y0 = y)); + Z1 += w * (z0 + (z0 = z)); + centroidPointCartesian(x0, y0, z0); +} + +function centroidLineEnd() { + centroidStream.point = centroidPoint; +} + +// See J. E. Brock, The Inertia Tensor for a Spherical Triangle, +// J. Applied Mechanics 42, 239 (1975). +function centroidRingStart() { + centroidStream.point = centroidRingPointFirst; +} + +function centroidRingEnd() { + centroidRingPoint(lambda00$2, phi00$2); + centroidStream.point = centroidPoint; +} + +function centroidRingPointFirst(lambda, phi) { + lambda00$2 = lambda, phi00$2 = phi; + lambda *= radians, phi *= radians; + centroidStream.point = centroidRingPoint; + var cosPhi = cos$1(phi); + x0 = cosPhi * cos$1(lambda); + y0 = cosPhi * sin$1(lambda); + z0 = sin$1(phi); + centroidPointCartesian(x0, y0, z0); +} + +function centroidRingPoint(lambda, phi) { + lambda *= radians, phi *= radians; + var cosPhi = cos$1(phi), + x = cosPhi * cos$1(lambda), + y = cosPhi * sin$1(lambda), + z = sin$1(phi), + cx = y0 * z - z0 * y, + cy = z0 * x - x0 * z, + cz = x0 * y - y0 * x, + m = sqrt(cx * cx + cy * cy + cz * cz), + w = asin(m), // line weight = angle + v = m && -w / m; // area weight multiplier + X2 += v * cx; + Y2 += v * cy; + Z2 += v * cz; + W1 += w; + X1 += w * (x0 + (x0 = x)); + Y1 += w * (y0 + (y0 = y)); + Z1 += w * (z0 + (z0 = z)); + centroidPointCartesian(x0, y0, z0); +} + +function centroid(object) { + W0 = W1 = + X0 = Y0 = Z0 = + X1 = Y1 = Z1 = + X2 = Y2 = Z2 = 0; + geoStream(object, centroidStream); + + var x = X2, + y = Y2, + z = Z2, + m = x * x + y * y + z * z; + + // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid. + if (m < epsilon2$1) { + x = X1, y = Y1, z = Z1; + // If the feature has zero length, fall back to arithmetic mean of point vectors. + if (W1 < epsilon$2) x = X0, y = Y0, z = Z0; + m = x * x + y * y + z * z; + // If the feature still has an undefined ccentroid, then return. + if (m < epsilon2$1) return [NaN, NaN]; + } + + return [atan2(y, x) * degrees$1, asin(z / sqrt(m)) * degrees$1]; +} + +function constant$7(x) { + return function() { + return x; + }; +} + +function compose(a, b) { + + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + + return compose; +} + +function rotationIdentity(lambda, phi) { + return [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi]; +} + +rotationIdentity.invert = rotationIdentity; + +function rotateRadians(deltaLambda, deltaPhi, deltaGamma) { + return (deltaLambda %= tau$3) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) + : rotationLambda(deltaLambda)) + : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) + : rotationIdentity); +} + +function forwardRotationLambda(deltaLambda) { + return function(lambda, phi) { + return lambda += deltaLambda, [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi]; + }; +} + +function rotationLambda(deltaLambda) { + var rotation = forwardRotationLambda(deltaLambda); + rotation.invert = forwardRotationLambda(-deltaLambda); + return rotation; +} + +function rotationPhiGamma(deltaPhi, deltaGamma) { + var cosDeltaPhi = cos$1(deltaPhi), + sinDeltaPhi = sin$1(deltaPhi), + cosDeltaGamma = cos$1(deltaGamma), + sinDeltaGamma = sin$1(deltaGamma); + + function rotation(lambda, phi) { + var cosPhi = cos$1(phi), + x = cos$1(lambda) * cosPhi, + y = sin$1(lambda) * cosPhi, + z = sin$1(phi), + k = z * cosDeltaPhi + x * sinDeltaPhi; + return [ + atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), + asin(k * cosDeltaGamma + y * sinDeltaGamma) + ]; + } + + rotation.invert = function(lambda, phi) { + var cosPhi = cos$1(phi), + x = cos$1(lambda) * cosPhi, + y = sin$1(lambda) * cosPhi, + z = sin$1(phi), + k = z * cosDeltaGamma - y * sinDeltaGamma; + return [ + atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), + asin(k * cosDeltaPhi - x * sinDeltaPhi) + ]; + }; + + return rotation; +} + +function rotation(rotate) { + rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0); + + function forward(coordinates) { + coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians); + return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates; + } + + forward.invert = function(coordinates) { + coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians); + return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates; + }; + + return forward; +} + +// Generates a circle centered at [0°, 0°], with a given radius and precision. +function circleStream(stream, radius, delta, direction, t0, t1) { + if (!delta) return; + var cosRadius = cos$1(radius), + sinRadius = sin$1(radius), + step = direction * delta; + if (t0 == null) { + t0 = radius + direction * tau$3; + t1 = radius - step / 2; + } else { + t0 = circleRadius(cosRadius, t0); + t1 = circleRadius(cosRadius, t1); + if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau$3; + } + for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) { + point = spherical([cosRadius, -sinRadius * cos$1(t), -sinRadius * sin$1(t)]); + stream.point(point[0], point[1]); + } +} + +// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0]. +function circleRadius(cosRadius, point) { + point = cartesian(point), point[0] -= cosRadius; + cartesianNormalizeInPlace(point); + var radius = acos(-point[1]); + return ((-point[2] < 0 ? -radius : radius) + tau$3 - epsilon$2) % tau$3; +} + +function circle() { + var center = constant$7([0, 0]), + radius = constant$7(90), + precision = constant$7(6), + ring, + rotate, + stream = {point: point}; + + function point(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= degrees$1, x[1] *= degrees$1; + } + + function circle() { + var c = center.apply(this, arguments), + r = radius.apply(this, arguments) * radians, + p = precision.apply(this, arguments) * radians; + ring = []; + rotate = rotateRadians(-c[0] * radians, -c[1] * radians, 0).invert; + circleStream(stream, r, p, 1); + c = {type: "Polygon", coordinates: [ring]}; + ring = rotate = null; + return c; + } + + circle.center = function(_) { + return arguments.length ? (center = typeof _ === "function" ? _ : constant$7([+_[0], +_[1]]), circle) : center; + }; + + circle.radius = function(_) { + return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), circle) : radius; + }; + + circle.precision = function(_) { + return arguments.length ? (precision = typeof _ === "function" ? _ : constant$7(+_), circle) : precision; + }; + + return circle; +} + +function clipBuffer() { + var lines = [], + line; + return { + point: function(x, y) { + line.push([x, y]); + }, + lineStart: function() { + lines.push(line = []); + }, + lineEnd: noop$1, + rejoin: function() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + }, + result: function() { + var result = lines; + lines = []; + line = null; + return result; + } + }; +} + +function pointEqual(a, b) { + return abs(a[0] - b[0]) < epsilon$2 && abs(a[1] - b[1]) < epsilon$2; +} + +function Intersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; // another intersection + this.e = entry; // is an entry? + this.v = false; // visited + this.n = this.p = null; // next & previous +} + +// A generalized polygon clipping algorithm: given a polygon that has been cut +// into its visible line segments, and rejoins the segments by interpolating +// along the clip edge. +function clipRejoin(segments, compareIntersection, startInside, interpolate, stream) { + var subject = [], + clip = [], + i, + n; + + segments.forEach(function(segment) { + if ((n = segment.length - 1) <= 0) return; + var n, p0 = segment[0], p1 = segment[n], x; + + // If the first and last points of a segment are coincident, then treat as a + // closed ring. TODO if all rings are closed, then the winding order of the + // exterior ring should be checked. + if (pointEqual(p0, p1)) { + stream.lineStart(); + for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]); + stream.lineEnd(); + return; + } + + subject.push(x = new Intersection(p0, segment, null, true)); + clip.push(x.o = new Intersection(p0, null, x, false)); + subject.push(x = new Intersection(p1, segment, null, false)); + clip.push(x.o = new Intersection(p1, null, x, true)); + }); + + if (!subject.length) return; + + clip.sort(compareIntersection); + link$1(subject); + link$1(clip); + + for (i = 0, n = clip.length; i < n; ++i) { + clip[i].e = startInside = !startInside; + } + + var start = subject[0], + points, + point; + + while (1) { + // Find first unvisited intersection. + var current = start, + isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; + stream.lineStart(); + do { + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.n.x, 1, stream); + } + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.p.x, -1, stream); + } + current = current.p; + } + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + stream.lineEnd(); + } +} + +function link$1(array) { + if (!(n = array.length)) return; + var n, + i = 0, + a = array[0], + b; + while (++i < n) { + a.n = b = array[i]; + b.p = a; + a = b; + } + a.n = b = array[0]; + b.p = a; +} + +var sum$1 = adder(); + +function polygonContains(polygon, point) { + var lambda = point[0], + phi = point[1], + normal = [sin$1(lambda), -cos$1(lambda), 0], + angle = 0, + winding = 0; + + sum$1.reset(); + + for (var i = 0, n = polygon.length; i < n; ++i) { + if (!(m = (ring = polygon[i]).length)) continue; + var ring, + m, + point0 = ring[m - 1], + lambda0 = point0[0], + phi0 = point0[1] / 2 + quarterPi, + sinPhi0 = sin$1(phi0), + cosPhi0 = cos$1(phi0); + + for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) { + var point1 = ring[j], + lambda1 = point1[0], + phi1 = point1[1] / 2 + quarterPi, + sinPhi1 = sin$1(phi1), + cosPhi1 = cos$1(phi1), + delta = lambda1 - lambda0, + sign$$1 = delta >= 0 ? 1 : -1, + absDelta = sign$$1 * delta, + antimeridian = absDelta > pi$3, + k = sinPhi0 * sinPhi1; + + sum$1.add(atan2(k * sign$$1 * sin$1(absDelta), cosPhi0 * cosPhi1 + k * cos$1(absDelta))); + angle += antimeridian ? delta + sign$$1 * tau$3 : delta; + + // Are the longitudes either side of the point’s meridian (lambda), + // and are the latitudes smaller than the parallel (phi)? + if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) { + var arc = cartesianCross(cartesian(point0), cartesian(point1)); + cartesianNormalizeInPlace(arc); + var intersection = cartesianCross(normal, arc); + cartesianNormalizeInPlace(intersection); + var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]); + if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) { + winding += antimeridian ^ delta >= 0 ? 1 : -1; + } + } + } + } + + // First, determine whether the South pole is inside or outside: + // + // It is inside if: + // * the polygon winds around it in a clockwise direction. + // * the polygon does not (cumulatively) wind around it, but has a negative + // (counter-clockwise) area. + // + // Second, count the (signed) number of times a segment crosses a lambda + // from the point to the South pole. If it is zero, then the point is the + // same side as the South pole. + + return (angle < -epsilon$2 || angle < epsilon$2 && sum$1 < -epsilon$2) ^ (winding & 1); +} + +function clip(pointVisible, clipLine, interpolate, start) { + return function(sink) { + var line = clipLine(sink), + ringBuffer = clipBuffer(), + ringSink = clipLine(ringBuffer), + polygonStarted = false, + polygon, + segments, + ring; + + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + segments = []; + polygon = []; + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = merge(segments); + var startInside = polygonContains(polygon, start); + if (segments.length) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + clipRejoin(segments, compareIntersection, startInside, interpolate, sink); + } else if (startInside) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + sink.lineStart(); + interpolate(null, null, 1, sink); + sink.lineEnd(); + } + if (polygonStarted) sink.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function() { + sink.polygonStart(); + sink.lineStart(); + interpolate(null, null, 1, sink); + sink.lineEnd(); + sink.polygonEnd(); + } + }; + + function point(lambda, phi) { + if (pointVisible(lambda, phi)) sink.point(lambda, phi); + } + + function pointLine(lambda, phi) { + line.point(lambda, phi); + } + + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + + function pointRing(lambda, phi) { + ring.push([lambda, phi]); + ringSink.point(lambda, phi); + } + + function ringStart() { + ringSink.lineStart(); + ring = []; + } + + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringSink.lineEnd(); + + var clean = ringSink.clean(), + ringSegments = ringBuffer.result(), + i, n = ringSegments.length, m, + segment, + point; + + ring.pop(); + polygon.push(ring); + ring = null; + + if (!n) return; + + // No intersections. + if (clean & 1) { + segment = ringSegments[0]; + if ((m = segment.length - 1) > 0) { + if (!polygonStarted) sink.polygonStart(), polygonStarted = true; + sink.lineStart(); + for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]); + sink.lineEnd(); + } + return; + } + + // Rejoin connected segments. + // TODO reuse ringBuffer.rejoin()? + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + + segments.push(ringSegments.filter(validSegment)); + } + + return clip; + }; +} + +function validSegment(segment) { + return segment.length > 1; +} + +// Intersections are sorted along the clip edge. For both antimeridian cutting +// and circle clipping, the same comparison is used. +function compareIntersection(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfPi$2 - epsilon$2 : halfPi$2 - a[1]) + - ((b = b.x)[0] < 0 ? b[1] - halfPi$2 - epsilon$2 : halfPi$2 - b[1]); +} + +var clipAntimeridian = clip( + function() { return true; }, + clipAntimeridianLine, + clipAntimeridianInterpolate, + [-pi$3, -halfPi$2] +); + +// Takes a line and cuts into visible segments. Return values: 0 - there were +// intersections or the line was empty; 1 - no intersections; 2 - there were +// intersections, and the first and last segments should be rejoined. +function clipAntimeridianLine(stream) { + var lambda0 = NaN, + phi0 = NaN, + sign0 = NaN, + clean; // no intersections + + return { + lineStart: function() { + stream.lineStart(); + clean = 1; + }, + point: function(lambda1, phi1) { + var sign1 = lambda1 > 0 ? pi$3 : -pi$3, + delta = abs(lambda1 - lambda0); + if (abs(delta - pi$3) < epsilon$2) { // line crosses a pole + stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi$2 : -halfPi$2); + stream.point(sign0, phi0); + stream.lineEnd(); + stream.lineStart(); + stream.point(sign1, phi0); + stream.point(lambda1, phi0); + clean = 0; + } else if (sign0 !== sign1 && delta >= pi$3) { // line crosses antimeridian + if (abs(lambda0 - sign0) < epsilon$2) lambda0 -= sign0 * epsilon$2; // handle degeneracies + if (abs(lambda1 - sign1) < epsilon$2) lambda1 -= sign1 * epsilon$2; + phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1); + stream.point(sign0, phi0); + stream.lineEnd(); + stream.lineStart(); + stream.point(sign1, phi0); + clean = 0; + } + stream.point(lambda0 = lambda1, phi0 = phi1); + sign0 = sign1; + }, + lineEnd: function() { + stream.lineEnd(); + lambda0 = phi0 = NaN; + }, + clean: function() { + return 2 - clean; // if intersections, rejoin first and last segments + } + }; +} + +function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) { + var cosPhi0, + cosPhi1, + sinLambda0Lambda1 = sin$1(lambda0 - lambda1); + return abs(sinLambda0Lambda1) > epsilon$2 + ? atan((sin$1(phi0) * (cosPhi1 = cos$1(phi1)) * sin$1(lambda1) + - sin$1(phi1) * (cosPhi0 = cos$1(phi0)) * sin$1(lambda0)) + / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) + : (phi0 + phi1) / 2; +} + +function clipAntimeridianInterpolate(from, to, direction, stream) { + var phi; + if (from == null) { + phi = direction * halfPi$2; + stream.point(-pi$3, phi); + stream.point(0, phi); + stream.point(pi$3, phi); + stream.point(pi$3, 0); + stream.point(pi$3, -phi); + stream.point(0, -phi); + stream.point(-pi$3, -phi); + stream.point(-pi$3, 0); + stream.point(-pi$3, phi); + } else if (abs(from[0] - to[0]) > epsilon$2) { + var lambda = from[0] < to[0] ? pi$3 : -pi$3; + phi = direction * lambda / 2; + stream.point(-lambda, phi); + stream.point(0, phi); + stream.point(lambda, phi); + } else { + stream.point(to[0], to[1]); + } +} + +function clipCircle(radius) { + var cr = cos$1(radius), + delta = 6 * radians, + smallRadius = cr > 0, + notHemisphere = abs(cr) > epsilon$2; // TODO optimise for this common case + + function interpolate(from, to, direction, stream) { + circleStream(stream, radius, delta, direction, from, to); + } + + function visible(lambda, phi) { + return cos$1(lambda) * cos$1(phi) > cr; + } + + // Takes a line and cuts into visible segments. Return values used for polygon + // clipping: 0 - there were intersections or the line was empty; 1 - no + // intersections 2 - there were intersections, and the first and last segments + // should be rejoined. + function clipLine(stream) { + var point0, // previous point + c0, // code for previous point + v0, // visibility of previous point + v00, // visibility of first point + clean; // no intersections + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(lambda, phi) { + var point1 = [lambda, phi], + point2, + v = visible(lambda, phi), + c = smallRadius + ? v ? 0 : code(lambda, phi) + : v ? code(lambda + (lambda < 0 ? pi$3 : -pi$3), phi) : 0; + if (!point0 && (v00 = v0 = v)) stream.lineStart(); + // Handle degeneracies. + // TODO ignore if not clipping polygons. + if (v !== v0) { + point2 = intersect(point0, point1); + if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) { + point1[0] += epsilon$2; + point1[1] += epsilon$2; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v) { + // outside going in + stream.lineStart(); + point2 = intersect(point1, point0); + stream.point(point2[0], point2[1]); + } else { + // inside going out + point2 = intersect(point0, point1); + stream.point(point2[0], point2[1]); + stream.lineEnd(); + } + point0 = point2; + } else if (notHemisphere && point0 && smallRadius ^ v) { + var t; + // If the codes for two points are different, or are both zero, + // and there this segment intersects with the small circle. + if (!(c & c0) && (t = intersect(point1, point0, true))) { + clean = 0; + if (smallRadius) { + stream.lineStart(); + stream.point(t[0][0], t[0][1]); + stream.point(t[1][0], t[1][1]); + stream.lineEnd(); + } else { + stream.point(t[1][0], t[1][1]); + stream.lineEnd(); + stream.lineStart(); + stream.point(t[0][0], t[0][1]); + } + } + } + if (v && (!point0 || !pointEqual(point0, point1))) { + stream.point(point1[0], point1[1]); + } + point0 = point1, v0 = v, c0 = c; + }, + lineEnd: function() { + if (v0) stream.lineEnd(); + point0 = null; + }, + // Rejoin first and last segments if there were intersections and the first + // and last points were visible. + clean: function() { + return clean | ((v00 && v0) << 1); + } + }; + } + + // Intersects the great circle between a and b with the clip circle. + function intersect(a, b, two) { + var pa = cartesian(a), + pb = cartesian(b); + + // We have two planes, n1.p = d1 and n2.p = d2. + // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2). + var n1 = [1, 0, 0], // normal + n2 = cartesianCross(pa, pb), + n2n2 = cartesianDot(n2, n2), + n1n2 = n2[0], // cartesianDot(n1, n2), + determinant = n2n2 - n1n2 * n1n2; + + // Two polar points. + if (!determinant) return !two && a; + + var c1 = cr * n2n2 / determinant, + c2 = -cr * n1n2 / determinant, + n1xn2 = cartesianCross(n1, n2), + A = cartesianScale(n1, c1), + B = cartesianScale(n2, c2); + cartesianAddInPlace(A, B); + + // Solve |p(t)|^2 = 1. + var u = n1xn2, + w = cartesianDot(A, u), + uu = cartesianDot(u, u), + t2 = w * w - uu * (cartesianDot(A, A) - 1); + + if (t2 < 0) return; + + var t = sqrt(t2), + q = cartesianScale(u, (-w - t) / uu); + cartesianAddInPlace(q, A); + q = spherical(q); + + if (!two) return q; + + // Two intersection points. + var lambda0 = a[0], + lambda1 = b[0], + phi0 = a[1], + phi1 = b[1], + z; + + if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z; + + var delta = lambda1 - lambda0, + polar = abs(delta - pi$3) < epsilon$2, + meridian = polar || delta < epsilon$2; + + if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; + + // Check that the first point is between a and b. + if (meridian + ? polar + ? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon$2 ? phi0 : phi1) + : phi0 <= q[1] && q[1] <= phi1 + : delta > pi$3 ^ (lambda0 <= q[0] && q[0] <= lambda1)) { + var q1 = cartesianScale(u, (-w + t) / uu); + cartesianAddInPlace(q1, A); + return [q, spherical(q1)]; + } + } + + // Generates a 4-bit vector representing the location of a point relative to + // the small circle's bounding box. + function code(lambda, phi) { + var r = smallRadius ? radius : pi$3 - radius, + code = 0; + if (lambda < -r) code |= 1; // left + else if (lambda > r) code |= 2; // right + if (phi < -r) code |= 4; // below + else if (phi > r) code |= 8; // above + return code; + } + + return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi$3, radius - pi$3]); +} + +function clipLine(a, b, x0, y0, x1, y1) { + var ax = a[0], + ay = a[1], + bx = b[0], + by = b[1], + t0 = 0, + t1 = 1, + dx = bx - ax, + dy = by - ay, + r; + + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy; + if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy; + return true; +} + +var clipMax = 1e9; +var clipMin = -clipMax; + +// TODO Use d3-polygon’s polygonContains here for the ring check? +// TODO Eliminate duplicate buffering in clipBuffer and polygon.push? + +function clipRectangle(x0, y0, x1, y1) { + + function visible(x, y) { + return x0 <= x && x <= x1 && y0 <= y && y <= y1; + } + + function interpolate(from, to, direction, stream) { + var a = 0, a1 = 0; + if (from == null + || (a = corner(from, direction)) !== (a1 = corner(to, direction)) + || comparePoint(from, to) < 0 ^ direction > 0) { + do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); + while ((a = (a + direction + 4) % 4) !== a1); + } else { + stream.point(to[0], to[1]); + } + } + + function corner(p, direction) { + return abs(p[0] - x0) < epsilon$2 ? direction > 0 ? 0 : 3 + : abs(p[0] - x1) < epsilon$2 ? direction > 0 ? 2 : 1 + : abs(p[1] - y0) < epsilon$2 ? direction > 0 ? 1 : 0 + : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon + } + + function compareIntersection(a, b) { + return comparePoint(a.x, b.x); + } + + function comparePoint(a, b) { + var ca = corner(a, 1), + cb = corner(b, 1); + return ca !== cb ? ca - cb + : ca === 0 ? b[1] - a[1] + : ca === 1 ? a[0] - b[0] + : ca === 2 ? a[1] - b[1] + : b[0] - a[0]; + } + + return function(stream) { + var activeStream = stream, + bufferStream = clipBuffer(), + segments, + polygon, + ring, + x__, y__, v__, // first point + x_, y_, v_, // previous point + first, + clean; + + var clipStream = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: polygonStart, + polygonEnd: polygonEnd + }; + + function point(x, y) { + if (visible(x, y)) activeStream.point(x, y); + } + + function polygonInside() { + var winding = 0; + + for (var i = 0, n = polygon.length; i < n; ++i) { + for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) { + a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1]; + if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; } + else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; } + } + } + + return winding; + } + + // Buffer geometry within a polygon and then clip it en masse. + function polygonStart() { + activeStream = bufferStream, segments = [], polygon = [], clean = true; + } + + function polygonEnd() { + var startInside = polygonInside(), + cleanInside = clean && startInside, + visible = (segments = merge(segments)).length; + if (cleanInside || visible) { + stream.polygonStart(); + if (cleanInside) { + stream.lineStart(); + interpolate(null, null, 1, stream); + stream.lineEnd(); + } + if (visible) { + clipRejoin(segments, compareIntersection, startInside, interpolate, stream); + } + stream.polygonEnd(); + } + activeStream = stream, segments = polygon = ring = null; + } + + function lineStart() { + clipStream.point = linePoint; + if (polygon) polygon.push(ring = []); + first = true; + v_ = false; + x_ = y_ = NaN; + } + + // TODO rather than special-case polygons, simply handle them separately. + // Ideally, coincident intersection points should be jittered to avoid + // clipping issues. + function lineEnd() { + if (segments) { + linePoint(x__, y__); + if (v__ && v_) bufferStream.rejoin(); + segments.push(bufferStream.result()); + } + clipStream.point = point; + if (v_) activeStream.lineEnd(); + } + + function linePoint(x, y) { + var v = visible(x, y); + if (polygon) ring.push([x, y]); + if (first) { + x__ = x, y__ = y, v__ = v; + first = false; + if (v) { + activeStream.lineStart(); + activeStream.point(x, y); + } + } else { + if (v && v_) activeStream.point(x, y); + else { + var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))], + b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))]; + if (clipLine(a, b, x0, y0, x1, y1)) { + if (!v_) { + activeStream.lineStart(); + activeStream.point(a[0], a[1]); + } + activeStream.point(b[0], b[1]); + if (!v) activeStream.lineEnd(); + clean = false; + } else if (v) { + activeStream.lineStart(); + activeStream.point(x, y); + clean = false; + } + } + } + x_ = x, y_ = y, v_ = v; + } + + return clipStream; + }; +} + +function extent$1() { + var x0 = 0, + y0 = 0, + x1 = 960, + y1 = 500, + cache, + cacheStream, + clip; + + return clip = { + stream: function(stream) { + return cache && cacheStream === stream ? cache : cache = clipRectangle(x0, y0, x1, y1)(cacheStream = stream); + }, + extent: function(_) { + return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]]; + } + }; +} + +var lengthSum = adder(); +var lambda0$2; +var sinPhi0$1; +var cosPhi0$1; + +var lengthStream = { + sphere: noop$1, + point: noop$1, + lineStart: lengthLineStart, + lineEnd: noop$1, + polygonStart: noop$1, + polygonEnd: noop$1 +}; + +function lengthLineStart() { + lengthStream.point = lengthPointFirst; + lengthStream.lineEnd = lengthLineEnd; +} + +function lengthLineEnd() { + lengthStream.point = lengthStream.lineEnd = noop$1; +} + +function lengthPointFirst(lambda, phi) { + lambda *= radians, phi *= radians; + lambda0$2 = lambda, sinPhi0$1 = sin$1(phi), cosPhi0$1 = cos$1(phi); + lengthStream.point = lengthPoint; +} + +function lengthPoint(lambda, phi) { + lambda *= radians, phi *= radians; + var sinPhi = sin$1(phi), + cosPhi = cos$1(phi), + delta = abs(lambda - lambda0$2), + cosDelta = cos$1(delta), + sinDelta = sin$1(delta), + x = cosPhi * sinDelta, + y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta, + z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta; + lengthSum.add(atan2(sqrt(x * x + y * y), z)); + lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi; +} + +function length$1(object) { + lengthSum.reset(); + geoStream(object, lengthStream); + return +lengthSum; +} + +var coordinates = [null, null]; +var object$1 = {type: "LineString", coordinates: coordinates}; + +function distance(a, b) { + coordinates[0] = a; + coordinates[1] = b; + return length$1(object$1); +} + +var containsObjectType = { + Feature: function(object, point) { + return containsGeometry(object.geometry, point); + }, + FeatureCollection: function(object, point) { + var features = object.features, i = -1, n = features.length; + while (++i < n) if (containsGeometry(features[i].geometry, point)) return true; + return false; + } +}; + +var containsGeometryType = { + Sphere: function() { + return true; + }, + Point: function(object, point) { + return containsPoint(object.coordinates, point); + }, + MultiPoint: function(object, point) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) if (containsPoint(coordinates[i], point)) return true; + return false; + }, + LineString: function(object, point) { + return containsLine(object.coordinates, point); + }, + MultiLineString: function(object, point) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) if (containsLine(coordinates[i], point)) return true; + return false; + }, + Polygon: function(object, point) { + return containsPolygon(object.coordinates, point); + }, + MultiPolygon: function(object, point) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) if (containsPolygon(coordinates[i], point)) return true; + return false; + }, + GeometryCollection: function(object, point) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) if (containsGeometry(geometries[i], point)) return true; + return false; + } +}; + +function containsGeometry(geometry, point) { + return geometry && containsGeometryType.hasOwnProperty(geometry.type) + ? containsGeometryType[geometry.type](geometry, point) + : false; +} + +function containsPoint(coordinates, point) { + return distance(coordinates, point) === 0; +} + +function containsLine(coordinates, point) { + var ab = distance(coordinates[0], coordinates[1]), + ao = distance(coordinates[0], point), + ob = distance(point, coordinates[1]); + return ao + ob <= ab + epsilon$2; +} + +function containsPolygon(coordinates, point) { + return !!polygonContains(coordinates.map(ringRadians), pointRadians(point)); +} + +function ringRadians(ring) { + return ring = ring.map(pointRadians), ring.pop(), ring; +} + +function pointRadians(point) { + return [point[0] * radians, point[1] * radians]; +} + +function contains(object, point) { + return (object && containsObjectType.hasOwnProperty(object.type) + ? containsObjectType[object.type] + : containsGeometry)(object, point); +} + +function graticuleX(y0, y1, dy) { + var y = sequence(y0, y1 - epsilon$2, dy).concat(y1); + return function(x) { return y.map(function(y) { return [x, y]; }); }; +} + +function graticuleY(x0, x1, dx) { + var x = sequence(x0, x1 - epsilon$2, dx).concat(x1); + return function(y) { return x.map(function(x) { return [x, y]; }); }; +} + +function graticule() { + var x1, x0, X1, X0, + y1, y0, Y1, Y0, + dx = 10, dy = dx, DX = 90, DY = 360, + x, y, X, Y, + precision = 2.5; + + function graticule() { + return {type: "MultiLineString", coordinates: lines()}; + } + + function lines() { + return sequence(ceil(X0 / DX) * DX, X1, DX).map(X) + .concat(sequence(ceil(Y0 / DY) * DY, Y1, DY).map(Y)) + .concat(sequence(ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return abs(x % DX) > epsilon$2; }).map(x)) + .concat(sequence(ceil(y0 / dy) * dy, y1, dy).filter(function(y) { return abs(y % DY) > epsilon$2; }).map(y)); + } + + graticule.lines = function() { + return lines().map(function(coordinates) { return {type: "LineString", coordinates: coordinates}; }); + }; + + graticule.outline = function() { + return { + type: "Polygon", + coordinates: [ + X(X0).concat( + Y(Y1).slice(1), + X(X1).reverse().slice(1), + Y(Y0).reverse().slice(1)) + ] + }; + }; + + graticule.extent = function(_) { + if (!arguments.length) return graticule.extentMinor(); + return graticule.extentMajor(_).extentMinor(_); + }; + + graticule.extentMajor = function(_) { + if (!arguments.length) return [[X0, Y0], [X1, Y1]]; + X0 = +_[0][0], X1 = +_[1][0]; + Y0 = +_[0][1], Y1 = +_[1][1]; + if (X0 > X1) _ = X0, X0 = X1, X1 = _; + if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; + return graticule.precision(precision); + }; + + graticule.extentMinor = function(_) { + if (!arguments.length) return [[x0, y0], [x1, y1]]; + x0 = +_[0][0], x1 = +_[1][0]; + y0 = +_[0][1], y1 = +_[1][1]; + if (x0 > x1) _ = x0, x0 = x1, x1 = _; + if (y0 > y1) _ = y0, y0 = y1, y1 = _; + return graticule.precision(precision); + }; + + graticule.step = function(_) { + if (!arguments.length) return graticule.stepMinor(); + return graticule.stepMajor(_).stepMinor(_); + }; + + graticule.stepMajor = function(_) { + if (!arguments.length) return [DX, DY]; + DX = +_[0], DY = +_[1]; + return graticule; + }; + + graticule.stepMinor = function(_) { + if (!arguments.length) return [dx, dy]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = graticuleX(y0, y1, 90); + y = graticuleY(x0, x1, precision); + X = graticuleX(Y0, Y1, 90); + Y = graticuleY(X0, X1, precision); + return graticule; + }; + + return graticule + .extentMajor([[-180, -90 + epsilon$2], [180, 90 - epsilon$2]]) + .extentMinor([[-180, -80 - epsilon$2], [180, 80 + epsilon$2]]); +} + +function graticule10() { + return graticule()(); +} + +function interpolate$1(a, b) { + var x0 = a[0] * radians, + y0 = a[1] * radians, + x1 = b[0] * radians, + y1 = b[1] * radians, + cy0 = cos$1(y0), + sy0 = sin$1(y0), + cy1 = cos$1(y1), + sy1 = sin$1(y1), + kx0 = cy0 * cos$1(x0), + ky0 = cy0 * sin$1(x0), + kx1 = cy1 * cos$1(x1), + ky1 = cy1 * sin$1(x1), + d = 2 * asin(sqrt(haversin(y1 - y0) + cy0 * cy1 * haversin(x1 - x0))), + k = sin$1(d); + + var interpolate = d ? function(t) { + var B = sin$1(t *= d) / k, + A = sin$1(d - t) / k, + x = A * kx0 + B * kx1, + y = A * ky0 + B * ky1, + z = A * sy0 + B * sy1; + return [ + atan2(y, x) * degrees$1, + atan2(z, sqrt(x * x + y * y)) * degrees$1 + ]; + } : function() { + return [x0 * degrees$1, y0 * degrees$1]; + }; + + interpolate.distance = d; + + return interpolate; +} + +function identity$4(x) { + return x; +} + +var areaSum$1 = adder(); +var areaRingSum$1 = adder(); +var x00; +var y00; +var x0$1; +var y0$1; + +var areaStream$1 = { + point: noop$1, + lineStart: noop$1, + lineEnd: noop$1, + polygonStart: function() { + areaStream$1.lineStart = areaRingStart$1; + areaStream$1.lineEnd = areaRingEnd$1; + }, + polygonEnd: function() { + areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$1; + areaSum$1.add(abs(areaRingSum$1)); + areaRingSum$1.reset(); + }, + result: function() { + var area = areaSum$1 / 2; + areaSum$1.reset(); + return area; + } +}; + +function areaRingStart$1() { + areaStream$1.point = areaPointFirst$1; +} + +function areaPointFirst$1(x, y) { + areaStream$1.point = areaPoint$1; + x00 = x0$1 = x, y00 = y0$1 = y; +} + +function areaPoint$1(x, y) { + areaRingSum$1.add(y0$1 * x - x0$1 * y); + x0$1 = x, y0$1 = y; +} + +function areaRingEnd$1() { + areaPoint$1(x00, y00); +} + +var x0$2 = Infinity; +var y0$2 = x0$2; +var x1 = -x0$2; +var y1 = x1; + +var boundsStream$1 = { + point: boundsPoint$1, + lineStart: noop$1, + lineEnd: noop$1, + polygonStart: noop$1, + polygonEnd: noop$1, + result: function() { + var bounds = [[x0$2, y0$2], [x1, y1]]; + x1 = y1 = -(y0$2 = x0$2 = Infinity); + return bounds; + } +}; + +function boundsPoint$1(x, y) { + if (x < x0$2) x0$2 = x; + if (x > x1) x1 = x; + if (y < y0$2) y0$2 = y; + if (y > y1) y1 = y; +} + +// TODO Enforce positive area for exterior, negative area for interior? + +var X0$1 = 0; +var Y0$1 = 0; +var Z0$1 = 0; +var X1$1 = 0; +var Y1$1 = 0; +var Z1$1 = 0; +var X2$1 = 0; +var Y2$1 = 0; +var Z2$1 = 0; +var x00$1; +var y00$1; +var x0$3; +var y0$3; + +var centroidStream$1 = { + point: centroidPoint$1, + lineStart: centroidLineStart$1, + lineEnd: centroidLineEnd$1, + polygonStart: function() { + centroidStream$1.lineStart = centroidRingStart$1; + centroidStream$1.lineEnd = centroidRingEnd$1; + }, + polygonEnd: function() { + centroidStream$1.point = centroidPoint$1; + centroidStream$1.lineStart = centroidLineStart$1; + centroidStream$1.lineEnd = centroidLineEnd$1; + }, + result: function() { + var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1] + : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1] + : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1] + : [NaN, NaN]; + X0$1 = Y0$1 = Z0$1 = + X1$1 = Y1$1 = Z1$1 = + X2$1 = Y2$1 = Z2$1 = 0; + return centroid; + } +}; + +function centroidPoint$1(x, y) { + X0$1 += x; + Y0$1 += y; + ++Z0$1; +} + +function centroidLineStart$1() { + centroidStream$1.point = centroidPointFirstLine; +} + +function centroidPointFirstLine(x, y) { + centroidStream$1.point = centroidPointLine; + centroidPoint$1(x0$3 = x, y0$3 = y); +} + +function centroidPointLine(x, y) { + var dx = x - x0$3, dy = y - y0$3, z = sqrt(dx * dx + dy * dy); + X1$1 += z * (x0$3 + x) / 2; + Y1$1 += z * (y0$3 + y) / 2; + Z1$1 += z; + centroidPoint$1(x0$3 = x, y0$3 = y); +} + +function centroidLineEnd$1() { + centroidStream$1.point = centroidPoint$1; +} + +function centroidRingStart$1() { + centroidStream$1.point = centroidPointFirstRing; +} + +function centroidRingEnd$1() { + centroidPointRing(x00$1, y00$1); +} + +function centroidPointFirstRing(x, y) { + centroidStream$1.point = centroidPointRing; + centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y); +} + +function centroidPointRing(x, y) { + var dx = x - x0$3, + dy = y - y0$3, + z = sqrt(dx * dx + dy * dy); + + X1$1 += z * (x0$3 + x) / 2; + Y1$1 += z * (y0$3 + y) / 2; + Z1$1 += z; + + z = y0$3 * x - x0$3 * y; + X2$1 += z * (x0$3 + x); + Y2$1 += z * (y0$3 + y); + Z2$1 += z * 3; + centroidPoint$1(x0$3 = x, y0$3 = y); +} + +function PathContext(context) { + this._context = context; +} + +PathContext.prototype = { + _radius: 4.5, + pointRadius: function(_) { + return this._radius = _, this; + }, + polygonStart: function() { + this._line = 0; + }, + polygonEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._point = 0; + }, + lineEnd: function() { + if (this._line === 0) this._context.closePath(); + this._point = NaN; + }, + point: function(x, y) { + switch (this._point) { + case 0: { + this._context.moveTo(x, y); + this._point = 1; + break; + } + case 1: { + this._context.lineTo(x, y); + break; + } + default: { + this._context.moveTo(x + this._radius, y); + this._context.arc(x, y, this._radius, 0, tau$3); + break; + } + } + }, + result: noop$1 +}; + +var lengthSum$1 = adder(); +var lengthRing; +var x00$2; +var y00$2; +var x0$4; +var y0$4; + +var lengthStream$1 = { + point: noop$1, + lineStart: function() { + lengthStream$1.point = lengthPointFirst$1; + }, + lineEnd: function() { + if (lengthRing) lengthPoint$1(x00$2, y00$2); + lengthStream$1.point = noop$1; + }, + polygonStart: function() { + lengthRing = true; + }, + polygonEnd: function() { + lengthRing = null; + }, + result: function() { + var length = +lengthSum$1; + lengthSum$1.reset(); + return length; + } +}; + +function lengthPointFirst$1(x, y) { + lengthStream$1.point = lengthPoint$1; + x00$2 = x0$4 = x, y00$2 = y0$4 = y; +} + +function lengthPoint$1(x, y) { + x0$4 -= x, y0$4 -= y; + lengthSum$1.add(sqrt(x0$4 * x0$4 + y0$4 * y0$4)); + x0$4 = x, y0$4 = y; +} + +function PathString() { + this._string = []; +} + +PathString.prototype = { + _radius: 4.5, + _circle: circle$1(4.5), + pointRadius: function(_) { + if ((_ = +_) !== this._radius) this._radius = _, this._circle = null; + return this; + }, + polygonStart: function() { + this._line = 0; + }, + polygonEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._point = 0; + }, + lineEnd: function() { + if (this._line === 0) this._string.push("Z"); + this._point = NaN; + }, + point: function(x, y) { + switch (this._point) { + case 0: { + this._string.push("M", x, ",", y); + this._point = 1; + break; + } + case 1: { + this._string.push("L", x, ",", y); + break; + } + default: { + if (this._circle == null) this._circle = circle$1(this._radius); + this._string.push("M", x, ",", y, this._circle); + break; + } + } + }, + result: function() { + if (this._string.length) { + var result = this._string.join(""); + this._string = []; + return result; + } else { + return null; + } + } +}; + +function circle$1(radius) { + return "m0," + radius + + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + + "z"; +} + +function index$1(projection, context) { + var pointRadius = 4.5, + projectionStream, + contextStream; + + function path(object) { + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + geoStream(object, projectionStream(contextStream)); + } + return contextStream.result(); + } + + path.area = function(object) { + geoStream(object, projectionStream(areaStream$1)); + return areaStream$1.result(); + }; + + path.measure = function(object) { + geoStream(object, projectionStream(lengthStream$1)); + return lengthStream$1.result(); + }; + + path.bounds = function(object) { + geoStream(object, projectionStream(boundsStream$1)); + return boundsStream$1.result(); + }; + + path.centroid = function(object) { + geoStream(object, projectionStream(centroidStream$1)); + return centroidStream$1.result(); + }; + + path.projection = function(_) { + return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection; + }; + + path.context = function(_) { + if (!arguments.length) return context; + contextStream = _ == null ? (context = null, new PathString) : new PathContext(context = _); + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return path; + }; + + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); + return path; + }; + + return path.projection(projection).context(context); +} + +function transform(methods) { + return { + stream: transformer(methods) + }; +} + +function transformer(methods) { + return function(stream) { + var s = new TransformStream; + for (var key in methods) s[key] = methods[key]; + s.stream = stream; + return s; + }; +} + +function TransformStream() {} + +TransformStream.prototype = { + constructor: TransformStream, + point: function(x, y) { this.stream.point(x, y); }, + sphere: function() { this.stream.sphere(); }, + lineStart: function() { this.stream.lineStart(); }, + lineEnd: function() { this.stream.lineEnd(); }, + polygonStart: function() { this.stream.polygonStart(); }, + polygonEnd: function() { this.stream.polygonEnd(); } +}; + +function fit(projection, fitBounds, object) { + var clip = projection.clipExtent && projection.clipExtent(); + projection.scale(150).translate([0, 0]); + if (clip != null) projection.clipExtent(null); + geoStream(object, projection.stream(boundsStream$1)); + fitBounds(boundsStream$1.result()); + if (clip != null) projection.clipExtent(clip); + return projection; +} + +function fitExtent(projection, extent, object) { + return fit(projection, function(b) { + var w = extent[1][0] - extent[0][0], + h = extent[1][1] - extent[0][1], + k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])), + x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2, + y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2; + projection.scale(150 * k).translate([x, y]); + }, object); +} + +function fitSize(projection, size, object) { + return fitExtent(projection, [[0, 0], size], object); +} + +function fitWidth(projection, width, object) { + return fit(projection, function(b) { + var w = +width, + k = w / (b[1][0] - b[0][0]), + x = (w - k * (b[1][0] + b[0][0])) / 2, + y = -k * b[0][1]; + projection.scale(150 * k).translate([x, y]); + }, object); +} + +function fitHeight(projection, height, object) { + return fit(projection, function(b) { + var h = +height, + k = h / (b[1][1] - b[0][1]), + x = -k * b[0][0], + y = (h - k * (b[1][1] + b[0][1])) / 2; + projection.scale(150 * k).translate([x, y]); + }, object); +} + +var maxDepth = 16; +var cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance) + +function resample(project, delta2) { + return +delta2 ? resample$1(project, delta2) : resampleNone(project); +} + +function resampleNone(project) { + return transformer({ + point: function(x, y) { + x = project(x, y); + this.stream.point(x[0], x[1]); + } + }); +} + +function resample$1(project, delta2) { + + function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, + dy = y1 - y0, + d2 = dx * dx + dy * dy; + if (d2 > 4 * delta2 && depth--) { + var a = a0 + a1, + b = b0 + b1, + c = c0 + c1, + m = sqrt(a * a + b * b + c * c), + phi2 = asin(c /= m), + lambda2 = abs(abs(c) - 1) < epsilon$2 || abs(lambda0 - lambda1) < epsilon$2 ? (lambda0 + lambda1) / 2 : atan2(b, a), + p = project(lambda2, phi2), + x2 = p[0], + y2 = p[1], + dx2 = x2 - x0, + dy2 = y2 - y0, + dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > delta2 // perpendicular projected distance + || abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end + || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream); + } + } + } + return function(stream) { + var lambda00, x00, y00, a00, b00, c00, // first point + lambda0, x0, y0, a0, b0, c0; // previous point + + var resampleStream = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; }, + polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; } + }; + + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + + function lineStart() { + x0 = NaN; + resampleStream.point = linePoint; + stream.lineStart(); + } + + function linePoint(lambda, phi) { + var c = cartesian([lambda, phi]), p = project(lambda, phi); + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + + function lineEnd() { + resampleStream.point = point; + stream.lineEnd(); + } + + function ringStart() { + lineStart(); + resampleStream.point = ringPoint; + resampleStream.lineEnd = ringEnd; + } + + function ringPoint(lambda, phi) { + linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resampleStream.point = linePoint; + } + + function ringEnd() { + resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream); + resampleStream.lineEnd = lineEnd; + lineEnd(); + } + + return resampleStream; + }; +} + +var transformRadians = transformer({ + point: function(x, y) { + this.stream.point(x * radians, y * radians); + } +}); + +function transformRotate(rotate) { + return transformer({ + point: function(x, y) { + var r = rotate(x, y); + return this.stream.point(r[0], r[1]); + } + }); +} + +function projection(project) { + return projectionMutator(function() { return project; })(); +} + +function projectionMutator(projectAt) { + var project, + k = 150, // scale + x = 480, y = 250, // translate + dx, dy, lambda = 0, phi = 0, // center + deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, projectRotate, // rotate + theta = null, preclip = clipAntimeridian, // clip angle + x0 = null, y0, x1, y1, postclip = identity$4, // clip extent + delta2 = 0.5, projectResample = resample(projectTransform, delta2), // precision + cache, + cacheStream; + + function projection(point) { + point = projectRotate(point[0] * radians, point[1] * radians); + return [point[0] * k + dx, dy - point[1] * k]; + } + + function invert(point) { + point = projectRotate.invert((point[0] - dx) / k, (dy - point[1]) / k); + return point && [point[0] * degrees$1, point[1] * degrees$1]; + } + + function projectTransform(x, y) { + return x = project(x, y), [x[0] * k + dx, dy - x[1] * k]; + } + + projection.stream = function(stream) { + return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream))))); + }; + + projection.preclip = function(_) { + return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip; + }; + + projection.postclip = function(_) { + return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip; + }; + + projection.clipAngle = function(_) { + return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1; + }; + + projection.clipExtent = function(_) { + return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]]; + }; + + projection.scale = function(_) { + return arguments.length ? (k = +_, recenter()) : k; + }; + + projection.translate = function(_) { + return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y]; + }; + + projection.center = function(_) { + return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1]; + }; + + projection.rotate = function(_) { + return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1]; + }; + + projection.precision = function(_) { + return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2); + }; + + projection.fitExtent = function(extent, object) { + return fitExtent(projection, extent, object); + }; + + projection.fitSize = function(size, object) { + return fitSize(projection, size, object); + }; + + projection.fitWidth = function(width, object) { + return fitWidth(projection, width, object); + }; + + projection.fitHeight = function(height, object) { + return fitHeight(projection, height, object); + }; + + function recenter() { + projectRotate = compose(rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma), project); + var center = project(lambda, phi); + dx = x - center[0] * k; + dy = y + center[1] * k; + return reset(); + } + + function reset() { + cache = cacheStream = null; + return projection; + } + + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return recenter(); + }; +} + +function conicProjection(projectAt) { + var phi0 = 0, + phi1 = pi$3 / 3, + m = projectionMutator(projectAt), + p = m(phi0, phi1); + + p.parallels = function(_) { + return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees$1, phi1 * degrees$1]; + }; + + return p; +} + +function cylindricalEqualAreaRaw(phi0) { + var cosPhi0 = cos$1(phi0); + + function forward(lambda, phi) { + return [lambda * cosPhi0, sin$1(phi) / cosPhi0]; + } + + forward.invert = function(x, y) { + return [x / cosPhi0, asin(y * cosPhi0)]; + }; + + return forward; +} + +function conicEqualAreaRaw(y0, y1) { + var sy0 = sin$1(y0), n = (sy0 + sin$1(y1)) / 2; + + // Are the parallels symmetrical around the Equator? + if (abs(n) < epsilon$2) return cylindricalEqualAreaRaw(y0); + + var c = 1 + sy0 * (2 * n - sy0), r0 = sqrt(c) / n; + + function project(x, y) { + var r = sqrt(c - 2 * n * sin$1(y)) / n; + return [r * sin$1(x *= n), r0 - r * cos$1(x)]; + } + + project.invert = function(x, y) { + var r0y = r0 - y; + return [atan2(x, abs(r0y)) / n * sign(r0y), asin((c - (x * x + r0y * r0y) * n * n) / (2 * n))]; + }; + + return project; +} + +function conicEqualArea() { + return conicProjection(conicEqualAreaRaw) + .scale(155.424) + .center([0, 33.6442]); +} + +function albers() { + return conicEqualArea() + .parallels([29.5, 45.5]) + .scale(1070) + .translate([480, 250]) + .rotate([96, 0]) + .center([-0.6, 38.7]); +} + +// The projections must have mutually exclusive clip regions on the sphere, +// as this will avoid emitting interleaving lines and polygons. +function multiplex(streams) { + var n = streams.length; + return { + point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); }, + sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); }, + lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); }, + lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); }, + polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); }, + polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); } + }; +} + +// A composite projection for the United States, configured by default for +// 960×500. The projection also works quite well at 960×600 if you change the +// scale to 1285 and adjust the translate accordingly. The set of standard +// parallels for each region comes from USGS, which is published here: +// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers +function albersUsa() { + var cache, + cacheStream, + lower48 = albers(), lower48Point, + alaska = conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338 + hawaii = conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007 + point, pointStream = {point: function(x, y) { point = [x, y]; }}; + + function albersUsa(coordinates) { + var x = coordinates[0], y = coordinates[1]; + return point = null, (lower48Point.point(x, y), point) + || (alaskaPoint.point(x, y), point) + || (hawaiiPoint.point(x, y), point); + } + + albersUsa.invert = function(coordinates) { + var k = lower48.scale(), + t = lower48.translate(), + x = (coordinates[0] - t[0]) / k, + y = (coordinates[1] - t[1]) / k; + return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska + : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii + : lower48).invert(coordinates); + }; + + albersUsa.stream = function(stream) { + return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]); + }; + + albersUsa.precision = function(_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_), alaska.precision(_), hawaii.precision(_); + return reset(); + }; + + albersUsa.scale = function(_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_); + return albersUsa.translate(lower48.translate()); + }; + + albersUsa.translate = function(_) { + if (!arguments.length) return lower48.translate(); + var k = lower48.scale(), x = +_[0], y = +_[1]; + + lower48Point = lower48 + .translate(_) + .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]]) + .stream(pointStream); + + alaskaPoint = alaska + .translate([x - 0.307 * k, y + 0.201 * k]) + .clipExtent([[x - 0.425 * k + epsilon$2, y + 0.120 * k + epsilon$2], [x - 0.214 * k - epsilon$2, y + 0.234 * k - epsilon$2]]) + .stream(pointStream); + + hawaiiPoint = hawaii + .translate([x - 0.205 * k, y + 0.212 * k]) + .clipExtent([[x - 0.214 * k + epsilon$2, y + 0.166 * k + epsilon$2], [x - 0.115 * k - epsilon$2, y + 0.234 * k - epsilon$2]]) + .stream(pointStream); + + return reset(); + }; + + albersUsa.fitExtent = function(extent, object) { + return fitExtent(albersUsa, extent, object); + }; + + albersUsa.fitSize = function(size, object) { + return fitSize(albersUsa, size, object); + }; + + albersUsa.fitWidth = function(width, object) { + return fitWidth(albersUsa, width, object); + }; + + albersUsa.fitHeight = function(height, object) { + return fitHeight(albersUsa, height, object); + }; + + function reset() { + cache = cacheStream = null; + return albersUsa; + } + + return albersUsa.scale(1070); +} + +function azimuthalRaw(scale) { + return function(x, y) { + var cx = cos$1(x), + cy = cos$1(y), + k = scale(cx * cy); + return [ + k * cy * sin$1(x), + k * sin$1(y) + ]; + } +} + +function azimuthalInvert(angle) { + return function(x, y) { + var z = sqrt(x * x + y * y), + c = angle(z), + sc = sin$1(c), + cc = cos$1(c); + return [ + atan2(x * sc, z * cc), + asin(z && y * sc / z) + ]; + } +} + +var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) { + return sqrt(2 / (1 + cxcy)); +}); + +azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) { + return 2 * asin(z / 2); +}); + +function azimuthalEqualArea() { + return projection(azimuthalEqualAreaRaw) + .scale(124.75) + .clipAngle(180 - 1e-3); +} + +var azimuthalEquidistantRaw = azimuthalRaw(function(c) { + return (c = acos(c)) && c / sin$1(c); +}); + +azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) { + return z; +}); + +function azimuthalEquidistant() { + return projection(azimuthalEquidistantRaw) + .scale(79.4188) + .clipAngle(180 - 1e-3); +} + +function mercatorRaw(lambda, phi) { + return [lambda, log(tan((halfPi$2 + phi) / 2))]; +} + +mercatorRaw.invert = function(x, y) { + return [x, 2 * atan(exp(y)) - halfPi$2]; +}; + +function mercator() { + return mercatorProjection(mercatorRaw) + .scale(961 / tau$3); +} + +function mercatorProjection(project) { + var m = projection(project), + center = m.center, + scale = m.scale, + translate = m.translate, + clipExtent = m.clipExtent, + x0 = null, y0, x1, y1; // clip extent + + m.scale = function(_) { + return arguments.length ? (scale(_), reclip()) : scale(); + }; + + m.translate = function(_) { + return arguments.length ? (translate(_), reclip()) : translate(); + }; + + m.center = function(_) { + return arguments.length ? (center(_), reclip()) : center(); + }; + + m.clipExtent = function(_) { + return arguments.length ? (_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]]; + }; + + function reclip() { + var k = pi$3 * scale(), + t = m(rotation(m.rotate()).invert([0, 0])); + return clipExtent(x0 == null + ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw + ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]] + : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]); + } + + return reclip(); +} + +function tany(y) { + return tan((halfPi$2 + y) / 2); +} + +function conicConformalRaw(y0, y1) { + var cy0 = cos$1(y0), + n = y0 === y1 ? sin$1(y0) : log(cy0 / cos$1(y1)) / log(tany(y1) / tany(y0)), + f = cy0 * pow(tany(y0), n) / n; + + if (!n) return mercatorRaw; + + function project(x, y) { + if (f > 0) { if (y < -halfPi$2 + epsilon$2) y = -halfPi$2 + epsilon$2; } + else { if (y > halfPi$2 - epsilon$2) y = halfPi$2 - epsilon$2; } + var r = f / pow(tany(y), n); + return [r * sin$1(n * x), f - r * cos$1(n * x)]; + } + + project.invert = function(x, y) { + var fy = f - y, r = sign(n) * sqrt(x * x + fy * fy); + return [atan2(x, abs(fy)) / n * sign(fy), 2 * atan(pow(f / r, 1 / n)) - halfPi$2]; + }; + + return project; +} + +function conicConformal() { + return conicProjection(conicConformalRaw) + .scale(109.5) + .parallels([30, 30]); +} + +function equirectangularRaw(lambda, phi) { + return [lambda, phi]; +} + +equirectangularRaw.invert = equirectangularRaw; + +function equirectangular() { + return projection(equirectangularRaw) + .scale(152.63); +} + +function conicEquidistantRaw(y0, y1) { + var cy0 = cos$1(y0), + n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0), + g = cy0 / n + y0; + + if (abs(n) < epsilon$2) return equirectangularRaw; + + function project(x, y) { + var gy = g - y, nx = n * x; + return [gy * sin$1(nx), g - gy * cos$1(nx)]; + } + + project.invert = function(x, y) { + var gy = g - y; + return [atan2(x, abs(gy)) / n * sign(gy), g - sign(n) * sqrt(x * x + gy * gy)]; + }; + + return project; +} + +function conicEquidistant() { + return conicProjection(conicEquidistantRaw) + .scale(131.154) + .center([0, 13.9389]); +} + +function gnomonicRaw(x, y) { + var cy = cos$1(y), k = cos$1(x) * cy; + return [cy * sin$1(x) / k, sin$1(y) / k]; +} + +gnomonicRaw.invert = azimuthalInvert(atan); + +function gnomonic() { + return projection(gnomonicRaw) + .scale(144.049) + .clipAngle(60); +} + +function scaleTranslate(kx, ky, tx, ty) { + return kx === 1 && ky === 1 && tx === 0 && ty === 0 ? identity$4 : transformer({ + point: function(x, y) { + this.stream.point(x * kx + tx, y * ky + ty); + } + }); +} + +function identity$5() { + var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, transform$$1 = identity$4, // scale, translate and reflect + x0 = null, y0, x1, y1, // clip extent + postclip = identity$4, + cache, + cacheStream, + projection; + + function reset() { + cache = cacheStream = null; + return projection; + } + + return projection = { + stream: function(stream) { + return cache && cacheStream === stream ? cache : cache = transform$$1(postclip(cacheStream = stream)); + }, + postclip: function(_) { + return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip; + }, + clipExtent: function(_) { + return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]]; + }, + scale: function(_) { + return arguments.length ? (transform$$1 = scaleTranslate((k = +_) * sx, k * sy, tx, ty), reset()) : k; + }, + translate: function(_) { + return arguments.length ? (transform$$1 = scaleTranslate(k * sx, k * sy, tx = +_[0], ty = +_[1]), reset()) : [tx, ty]; + }, + reflectX: function(_) { + return arguments.length ? (transform$$1 = scaleTranslate(k * (sx = _ ? -1 : 1), k * sy, tx, ty), reset()) : sx < 0; + }, + reflectY: function(_) { + return arguments.length ? (transform$$1 = scaleTranslate(k * sx, k * (sy = _ ? -1 : 1), tx, ty), reset()) : sy < 0; + }, + fitExtent: function(extent, object) { + return fitExtent(projection, extent, object); + }, + fitSize: function(size, object) { + return fitSize(projection, size, object); + }, + fitWidth: function(width, object) { + return fitWidth(projection, width, object); + }, + fitHeight: function(height, object) { + return fitHeight(projection, height, object); + } + }; +} + +function naturalEarth1Raw(lambda, phi) { + var phi2 = phi * phi, phi4 = phi2 * phi2; + return [ + lambda * (0.8707 - 0.131979 * phi2 + phi4 * (-0.013791 + phi4 * (0.003971 * phi2 - 0.001529 * phi4))), + phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) + ]; +} + +naturalEarth1Raw.invert = function(x, y) { + var phi = y, i = 25, delta; + do { + var phi2 = phi * phi, phi4 = phi2 * phi2; + phi -= delta = (phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) - y) / + (1.007226 + phi2 * (0.015085 * 3 + phi4 * (-0.044475 * 7 + 0.028874 * 9 * phi2 - 0.005916 * 11 * phi4))); + } while (abs(delta) > epsilon$2 && --i > 0); + return [ + x / (0.8707 + (phi2 = phi * phi) * (-0.131979 + phi2 * (-0.013791 + phi2 * phi2 * phi2 * (0.003971 - 0.001529 * phi2)))), + phi + ]; +}; + +function naturalEarth1() { + return projection(naturalEarth1Raw) + .scale(175.295); +} + +function orthographicRaw(x, y) { + return [cos$1(y) * sin$1(x), sin$1(y)]; +} + +orthographicRaw.invert = azimuthalInvert(asin); + +function orthographic() { + return projection(orthographicRaw) + .scale(249.5) + .clipAngle(90 + epsilon$2); +} + +function stereographicRaw(x, y) { + var cy = cos$1(y), k = 1 + cos$1(x) * cy; + return [cy * sin$1(x) / k, sin$1(y) / k]; +} + +stereographicRaw.invert = azimuthalInvert(function(z) { + return 2 * atan(z); +}); + +function stereographic() { + return projection(stereographicRaw) + .scale(250) + .clipAngle(142); +} + +function transverseMercatorRaw(lambda, phi) { + return [log(tan((halfPi$2 + phi) / 2)), -lambda]; +} + +transverseMercatorRaw.invert = function(x, y) { + return [-y, 2 * atan(exp(x)) - halfPi$2]; +}; + +function transverseMercator() { + var m = mercatorProjection(transverseMercatorRaw), + center = m.center, + rotate = m.rotate; + + m.center = function(_) { + return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]); + }; + + m.rotate = function(_) { + return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]); + }; + + return rotate([0, 0, 90]) + .scale(159.155); +} + +function defaultSeparation(a, b) { + return a.parent === b.parent ? 1 : 2; +} + +function meanX(children) { + return children.reduce(meanXReduce, 0) / children.length; +} + +function meanXReduce(x, c) { + return x + c.x; +} + +function maxY(children) { + return 1 + children.reduce(maxYReduce, 0); +} + +function maxYReduce(y, c) { + return Math.max(y, c.y); +} + +function leafLeft(node) { + var children; + while (children = node.children) node = children[0]; + return node; +} + +function leafRight(node) { + var children; + while (children = node.children) node = children[children.length - 1]; + return node; +} + +function cluster() { + var separation = defaultSeparation, + dx = 1, + dy = 1, + nodeSize = false; + + function cluster(root) { + var previousNode, + x = 0; + + // First walk, computing the initial x & y values. + root.eachAfter(function(node) { + var children = node.children; + if (children) { + node.x = meanX(children); + node.y = maxY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + + var left = leafLeft(root), + right = leafRight(root), + x0 = left.x - separation(left, right) / 2, + x1 = right.x + separation(right, left) / 2; + + // Second walk, normalizing x & y to the desired size. + return root.eachAfter(nodeSize ? function(node) { + node.x = (node.x - root.x) * dx; + node.y = (root.y - node.y) * dy; + } : function(node) { + node.x = (node.x - x0) / (x1 - x0) * dx; + node.y = (1 - (root.y ? node.y / root.y : 1)) * dy; + }); + } + + cluster.separation = function(x) { + return arguments.length ? (separation = x, cluster) : separation; + }; + + cluster.size = function(x) { + return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]); + }; + + cluster.nodeSize = function(x) { + return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null); + }; + + return cluster; +} + +function count(node) { + var sum = 0, + children = node.children, + i = children && children.length; + if (!i) sum = 1; + else while (--i >= 0) sum += children[i].value; + node.value = sum; +} + +function node_count() { + return this.eachAfter(count); +} + +function node_each(callback) { + var node = this, current, next = [node], children, i, n; + do { + current = next.reverse(), next = []; + while (node = current.pop()) { + callback(node), children = node.children; + if (children) for (i = 0, n = children.length; i < n; ++i) { + next.push(children[i]); + } + } + } while (next.length); + return this; +} + +function node_eachBefore(callback) { + var node = this, nodes = [node], children, i; + while (node = nodes.pop()) { + callback(node), children = node.children; + if (children) for (i = children.length - 1; i >= 0; --i) { + nodes.push(children[i]); + } + } + return this; +} + +function node_eachAfter(callback) { + var node = this, nodes = [node], next = [], children, i, n; + while (node = nodes.pop()) { + next.push(node), children = node.children; + if (children) for (i = 0, n = children.length; i < n; ++i) { + nodes.push(children[i]); + } + } + while (node = next.pop()) { + callback(node); + } + return this; +} + +function node_sum(value) { + return this.eachAfter(function(node) { + var sum = +value(node.data) || 0, + children = node.children, + i = children && children.length; + while (--i >= 0) sum += children[i].value; + node.value = sum; + }); +} + +function node_sort(compare) { + return this.eachBefore(function(node) { + if (node.children) { + node.children.sort(compare); + } + }); +} + +function node_path(end) { + var start = this, + ancestor = leastCommonAncestor(start, end), + nodes = [start]; + while (start !== ancestor) { + start = start.parent; + nodes.push(start); + } + var k = nodes.length; + while (end !== ancestor) { + nodes.splice(k, 0, end); + end = end.parent; + } + return nodes; +} + +function leastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = a.ancestors(), + bNodes = b.ancestors(), + c = null; + a = aNodes.pop(); + b = bNodes.pop(); + while (a === b) { + c = a; + a = aNodes.pop(); + b = bNodes.pop(); + } + return c; +} + +function node_ancestors() { + var node = this, nodes = [node]; + while (node = node.parent) { + nodes.push(node); + } + return nodes; +} + +function node_descendants() { + var nodes = []; + this.each(function(node) { + nodes.push(node); + }); + return nodes; +} + +function node_leaves() { + var leaves = []; + this.eachBefore(function(node) { + if (!node.children) { + leaves.push(node); + } + }); + return leaves; +} + +function node_links() { + var root = this, links = []; + root.each(function(node) { + if (node !== root) { // Don’t include the root’s parent, if any. + links.push({source: node.parent, target: node}); + } + }); + return links; +} + +function hierarchy(data, children) { + var root = new Node(data), + valued = +data.value && (root.value = data.value), + node, + nodes = [root], + child, + childs, + i, + n; + + if (children == null) children = defaultChildren; + + while (node = nodes.pop()) { + if (valued) node.value = +node.data.value; + if ((childs = children(node.data)) && (n = childs.length)) { + node.children = new Array(n); + for (i = n - 1; i >= 0; --i) { + nodes.push(child = node.children[i] = new Node(childs[i])); + child.parent = node; + child.depth = node.depth + 1; + } + } + } + + return root.eachBefore(computeHeight); +} + +function node_copy() { + return hierarchy(this).eachBefore(copyData); +} + +function defaultChildren(d) { + return d.children; +} + +function copyData(node) { + node.data = node.data.data; +} + +function computeHeight(node) { + var height = 0; + do node.height = height; + while ((node = node.parent) && (node.height < ++height)); +} + +function Node(data) { + this.data = data; + this.depth = + this.height = 0; + this.parent = null; +} + +Node.prototype = hierarchy.prototype = { + constructor: Node, + count: node_count, + each: node_each, + eachAfter: node_eachAfter, + eachBefore: node_eachBefore, + sum: node_sum, + sort: node_sort, + path: node_path, + ancestors: node_ancestors, + descendants: node_descendants, + leaves: node_leaves, + links: node_links, + copy: node_copy +}; + +var slice$3 = Array.prototype.slice; + +function shuffle$1(array) { + var m = array.length, + t, + i; + + while (m) { + i = Math.random() * m-- | 0; + t = array[m]; + array[m] = array[i]; + array[i] = t; + } + + return array; +} + +function enclose(circles) { + var i = 0, n = (circles = shuffle$1(slice$3.call(circles))).length, B = [], p, e; + + while (i < n) { + p = circles[i]; + if (e && enclosesWeak(e, p)) ++i; + else e = encloseBasis(B = extendBasis(B, p)), i = 0; + } + + return e; +} + +function extendBasis(B, p) { + var i, j; + + if (enclosesWeakAll(p, B)) return [p]; + + // If we get here then B must have at least one element. + for (i = 0; i < B.length; ++i) { + if (enclosesNot(p, B[i]) + && enclosesWeakAll(encloseBasis2(B[i], p), B)) { + return [B[i], p]; + } + } + + // If we get here then B must have at least two elements. + for (i = 0; i < B.length - 1; ++i) { + for (j = i + 1; j < B.length; ++j) { + if (enclosesNot(encloseBasis2(B[i], B[j]), p) + && enclosesNot(encloseBasis2(B[i], p), B[j]) + && enclosesNot(encloseBasis2(B[j], p), B[i]) + && enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) { + return [B[i], B[j], p]; + } + } + } + + // If we get here then something is very wrong. + throw new Error; +} + +function enclosesNot(a, b) { + var dr = a.r - b.r, dx = b.x - a.x, dy = b.y - a.y; + return dr < 0 || dr * dr < dx * dx + dy * dy; +} + +function enclosesWeak(a, b) { + var dr = a.r - b.r + 1e-6, dx = b.x - a.x, dy = b.y - a.y; + return dr > 0 && dr * dr > dx * dx + dy * dy; +} + +function enclosesWeakAll(a, B) { + for (var i = 0; i < B.length; ++i) { + if (!enclosesWeak(a, B[i])) { + return false; + } + } + return true; +} + +function encloseBasis(B) { + switch (B.length) { + case 1: return encloseBasis1(B[0]); + case 2: return encloseBasis2(B[0], B[1]); + case 3: return encloseBasis3(B[0], B[1], B[2]); + } +} + +function encloseBasis1(a) { + return { + x: a.x, + y: a.y, + r: a.r + }; +} + +function encloseBasis2(a, b) { + var x1 = a.x, y1 = a.y, r1 = a.r, + x2 = b.x, y2 = b.y, r2 = b.r, + x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1, + l = Math.sqrt(x21 * x21 + y21 * y21); + return { + x: (x1 + x2 + x21 / l * r21) / 2, + y: (y1 + y2 + y21 / l * r21) / 2, + r: (l + r1 + r2) / 2 + }; +} + +function encloseBasis3(a, b, c) { + var x1 = a.x, y1 = a.y, r1 = a.r, + x2 = b.x, y2 = b.y, r2 = b.r, + x3 = c.x, y3 = c.y, r3 = c.r, + a2 = x1 - x2, + a3 = x1 - x3, + b2 = y1 - y2, + b3 = y1 - y3, + c2 = r2 - r1, + c3 = r3 - r1, + d1 = x1 * x1 + y1 * y1 - r1 * r1, + d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2, + d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3, + ab = a3 * b2 - a2 * b3, + xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1, + xb = (b3 * c2 - b2 * c3) / ab, + ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1, + yb = (a2 * c3 - a3 * c2) / ab, + A = xb * xb + yb * yb - 1, + B = 2 * (r1 + xa * xb + ya * yb), + C = xa * xa + ya * ya - r1 * r1, + r = -(A ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B); + return { + x: x1 + xa + xb * r, + y: y1 + ya + yb * r, + r: r + }; +} + +function place(a, b, c) { + var ax = a.x, + ay = a.y, + da = b.r + c.r, + db = a.r + c.r, + dx = b.x - ax, + dy = b.y - ay, + dc = dx * dx + dy * dy; + if (dc) { + var x = 0.5 + ((db *= db) - (da *= da)) / (2 * dc), + y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = ax + x * dx + y * dy; + c.y = ay + x * dy - y * dx; + } else { + c.x = ax + db; + c.y = ay; + } +} + +function intersects(a, b) { + var dx = b.x - a.x, + dy = b.y - a.y, + dr = a.r + b.r; + return dr * dr - 1e-6 > dx * dx + dy * dy; +} + +function score(node) { + var a = node._, + b = node.next._, + ab = a.r + b.r, + dx = (a.x * b.r + b.x * a.r) / ab, + dy = (a.y * b.r + b.y * a.r) / ab; + return dx * dx + dy * dy; +} + +function Node$1(circle) { + this._ = circle; + this.next = null; + this.previous = null; +} + +function packEnclose(circles) { + if (!(n = circles.length)) return 0; + + var a, b, c, n, aa, ca, i, j, k, sj, sk; + + // Place the first circle. + a = circles[0], a.x = 0, a.y = 0; + if (!(n > 1)) return a.r; + + // Place the second circle. + b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0; + if (!(n > 2)) return a.r + b.r; + + // Place the third circle. + place(b, a, c = circles[2]); + + // Initialize the front-chain using the first three circles a, b and c. + a = new Node$1(a), b = new Node$1(b), c = new Node$1(c); + a.next = c.previous = b; + b.next = a.previous = c; + c.next = b.previous = a; + + // Attempt to place each remaining circle… + pack: for (i = 3; i < n; ++i) { + place(a._, b._, c = circles[i]), c = new Node$1(c); + + // Find the closest intersecting circle on the front-chain, if any. + // “Closeness” is determined by linear distance along the front-chain. + // “Ahead” or “behind” is likewise determined by linear distance. + j = b.next, k = a.previous, sj = b._.r, sk = a._.r; + do { + if (sj <= sk) { + if (intersects(j._, c._)) { + b = j, a.next = b, b.previous = a, --i; + continue pack; + } + sj += j._.r, j = j.next; + } else { + if (intersects(k._, c._)) { + a = k, a.next = b, b.previous = a, --i; + continue pack; + } + sk += k._.r, k = k.previous; + } + } while (j !== k.next); + + // Success! Insert the new circle c between a and b. + c.previous = a, c.next = b, a.next = b.previous = b = c; + + // Compute the new closest circle pair to the centroid. + aa = score(a); + while ((c = c.next) !== b) { + if ((ca = score(c)) < aa) { + a = c, aa = ca; + } + } + b = a.next; + } + + // Compute the enclosing circle of the front chain. + a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = enclose(a); + + // Translate the circles to put the enclosing circle around the origin. + for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y; + + return c.r; +} + +function siblings(circles) { + packEnclose(circles); + return circles; +} + +function optional(f) { + return f == null ? null : required(f); +} + +function required(f) { + if (typeof f !== "function") throw new Error; + return f; +} + +function constantZero() { + return 0; +} + +function constant$8(x) { + return function() { + return x; + }; +} + +function defaultRadius$1(d) { + return Math.sqrt(d.value); +} + +function index$2() { + var radius = null, + dx = 1, + dy = 1, + padding = constantZero; + + function pack(root) { + root.x = dx / 2, root.y = dy / 2; + if (radius) { + root.eachBefore(radiusLeaf(radius)) + .eachAfter(packChildren(padding, 0.5)) + .eachBefore(translateChild(1)); + } else { + root.eachBefore(radiusLeaf(defaultRadius$1)) + .eachAfter(packChildren(constantZero, 1)) + .eachAfter(packChildren(padding, root.r / Math.min(dx, dy))) + .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r))); + } + return root; + } + + pack.radius = function(x) { + return arguments.length ? (radius = optional(x), pack) : radius; + }; + + pack.size = function(x) { + return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy]; + }; + + pack.padding = function(x) { + return arguments.length ? (padding = typeof x === "function" ? x : constant$8(+x), pack) : padding; + }; + + return pack; +} + +function radiusLeaf(radius) { + return function(node) { + if (!node.children) { + node.r = Math.max(0, +radius(node) || 0); + } + }; +} + +function packChildren(padding, k) { + return function(node) { + if (children = node.children) { + var children, + i, + n = children.length, + r = padding(node) * k || 0, + e; + + if (r) for (i = 0; i < n; ++i) children[i].r += r; + e = packEnclose(children); + if (r) for (i = 0; i < n; ++i) children[i].r -= r; + node.r = e + r; + } + }; +} + +function translateChild(k) { + return function(node) { + var parent = node.parent; + node.r *= k; + if (parent) { + node.x = parent.x + k * node.x; + node.y = parent.y + k * node.y; + } + }; +} + +function roundNode(node) { + node.x0 = Math.round(node.x0); + node.y0 = Math.round(node.y0); + node.x1 = Math.round(node.x1); + node.y1 = Math.round(node.y1); +} + +function treemapDice(parent, x0, y0, x1, y1) { + var nodes = parent.children, + node, + i = -1, + n = nodes.length, + k = parent.value && (x1 - x0) / parent.value; + + while (++i < n) { + node = nodes[i], node.y0 = y0, node.y1 = y1; + node.x0 = x0, node.x1 = x0 += node.value * k; + } +} + +function partition() { + var dx = 1, + dy = 1, + padding = 0, + round = false; + + function partition(root) { + var n = root.height + 1; + root.x0 = + root.y0 = padding; + root.x1 = dx; + root.y1 = dy / n; + root.eachBefore(positionNode(dy, n)); + if (round) root.eachBefore(roundNode); + return root; + } + + function positionNode(dy, n) { + return function(node) { + if (node.children) { + treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n); + } + var x0 = node.x0, + y0 = node.y0, + x1 = node.x1 - padding, + y1 = node.y1 - padding; + if (x1 < x0) x0 = x1 = (x0 + x1) / 2; + if (y1 < y0) y0 = y1 = (y0 + y1) / 2; + node.x0 = x0; + node.y0 = y0; + node.x1 = x1; + node.y1 = y1; + }; + } + + partition.round = function(x) { + return arguments.length ? (round = !!x, partition) : round; + }; + + partition.size = function(x) { + return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy]; + }; + + partition.padding = function(x) { + return arguments.length ? (padding = +x, partition) : padding; + }; + + return partition; +} + +var keyPrefix$1 = "$"; +var preroot = {depth: -1}; +var ambiguous = {}; + +function defaultId(d) { + return d.id; +} + +function defaultParentId(d) { + return d.parentId; +} + +function stratify() { + var id = defaultId, + parentId = defaultParentId; + + function stratify(data) { + var d, + i, + n = data.length, + root, + parent, + node, + nodes = new Array(n), + nodeId, + nodeKey, + nodeByKey = {}; + + for (i = 0; i < n; ++i) { + d = data[i], node = nodes[i] = new Node(d); + if ((nodeId = id(d, i, data)) != null && (nodeId += "")) { + nodeKey = keyPrefix$1 + (node.id = nodeId); + nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node; + } + } + + for (i = 0; i < n; ++i) { + node = nodes[i], nodeId = parentId(data[i], i, data); + if (nodeId == null || !(nodeId += "")) { + if (root) throw new Error("multiple roots"); + root = node; + } else { + parent = nodeByKey[keyPrefix$1 + nodeId]; + if (!parent) throw new Error("missing: " + nodeId); + if (parent === ambiguous) throw new Error("ambiguous: " + nodeId); + if (parent.children) parent.children.push(node); + else parent.children = [node]; + node.parent = parent; + } + } + + if (!root) throw new Error("no root"); + root.parent = preroot; + root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight); + root.parent = null; + if (n > 0) throw new Error("cycle"); + + return root; + } + + stratify.id = function(x) { + return arguments.length ? (id = required(x), stratify) : id; + }; + + stratify.parentId = function(x) { + return arguments.length ? (parentId = required(x), stratify) : parentId; + }; + + return stratify; +} + +function defaultSeparation$1(a, b) { + return a.parent === b.parent ? 1 : 2; +} + +// function radialSeparation(a, b) { +// return (a.parent === b.parent ? 1 : 2) / a.depth; +// } + +// This function is used to traverse the left contour of a subtree (or +// subforest). It returns the successor of v on this contour. This successor is +// either given by the leftmost child of v or by the thread of v. The function +// returns null if and only if v is on the highest level of its subtree. +function nextLeft(v) { + var children = v.children; + return children ? children[0] : v.t; +} + +// This function works analogously to nextLeft. +function nextRight(v) { + var children = v.children; + return children ? children[children.length - 1] : v.t; +} + +// Shifts the current subtree rooted at w+. This is done by increasing +// prelim(w+) and mod(w+) by shift. +function moveSubtree(wm, wp, shift) { + var change = shift / (wp.i - wm.i); + wp.c -= change; + wp.s += shift; + wm.c += change; + wp.z += shift; + wp.m += shift; +} + +// All other shifts, applied to the smaller subtrees between w- and w+, are +// performed by this function. To prepare the shifts, we have to adjust +// change(w+), shift(w+), and change(w-). +function executeShifts(v) { + var shift = 0, + change = 0, + children = v.children, + i = children.length, + w; + while (--i >= 0) { + w = children[i]; + w.z += shift; + w.m += shift; + shift += w.s + (change += w.c); + } +} + +// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise, +// returns the specified (default) ancestor. +function nextAncestor(vim, v, ancestor) { + return vim.a.parent === v.parent ? vim.a : ancestor; +} + +function TreeNode(node, i) { + this._ = node; + this.parent = null; + this.children = null; + this.A = null; // default ancestor + this.a = this; // ancestor + this.z = 0; // prelim + this.m = 0; // mod + this.c = 0; // change + this.s = 0; // shift + this.t = null; // thread + this.i = i; // number +} + +TreeNode.prototype = Object.create(Node.prototype); + +function treeRoot(root) { + var tree = new TreeNode(root, 0), + node, + nodes = [tree], + child, + children, + i, + n; + + while (node = nodes.pop()) { + if (children = node._.children) { + node.children = new Array(n = children.length); + for (i = n - 1; i >= 0; --i) { + nodes.push(child = node.children[i] = new TreeNode(children[i], i)); + child.parent = node; + } + } + } + + (tree.parent = new TreeNode(null, 0)).children = [tree]; + return tree; +} + +// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm +function tree() { + var separation = defaultSeparation$1, + dx = 1, + dy = 1, + nodeSize = null; + + function tree(root) { + var t = treeRoot(root); + + // Compute the layout using Buchheim et al.’s algorithm. + t.eachAfter(firstWalk), t.parent.m = -t.z; + t.eachBefore(secondWalk); + + // If a fixed node size is specified, scale x and y. + if (nodeSize) root.eachBefore(sizeNode); + + // If a fixed tree size is specified, scale x and y based on the extent. + // Compute the left-most, right-most, and depth-most nodes for extents. + else { + var left = root, + right = root, + bottom = root; + root.eachBefore(function(node) { + if (node.x < left.x) left = node; + if (node.x > right.x) right = node; + if (node.depth > bottom.depth) bottom = node; + }); + var s = left === right ? 1 : separation(left, right) / 2, + tx = s - left.x, + kx = dx / (right.x + s + tx), + ky = dy / (bottom.depth || 1); + root.eachBefore(function(node) { + node.x = (node.x + tx) * kx; + node.y = node.depth * ky; + }); + } + + return root; + } + + // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is + // applied recursively to the children of v, as well as the function + // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the + // node v is placed to the midpoint of its outermost children. + function firstWalk(v) { + var children = v.children, + siblings = v.parent.children, + w = v.i ? siblings[v.i - 1] : null; + if (children) { + executeShifts(v); + var midpoint = (children[0].z + children[children.length - 1].z) / 2; + if (w) { + v.z = w.z + separation(v._, w._); + v.m = v.z - midpoint; + } else { + v.z = midpoint; + } + } else if (w) { + v.z = w.z + separation(v._, w._); + } + v.parent.A = apportion(v, w, v.parent.A || siblings[0]); + } + + // Computes all real x-coordinates by summing up the modifiers recursively. + function secondWalk(v) { + v._.x = v.z + v.parent.m; + v.m += v.parent.m; + } + + // The core of the algorithm. Here, a new subtree is combined with the + // previous subtrees. Threads are used to traverse the inside and outside + // contours of the left and right subtree up to the highest common level. The + // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the + // superscript o means outside and i means inside, the subscript - means left + // subtree and + means right subtree. For summing up the modifiers along the + // contour, we use respective variables si+, si-, so-, and so+. Whenever two + // nodes of the inside contours conflict, we compute the left one of the + // greatest uncommon ancestors using the function ANCESTOR and call MOVE + // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees. + // Finally, we add a new thread (if necessary). + function apportion(v, w, ancestor) { + if (w) { + var vip = v, + vop = v, + vim = w, + vom = vip.parent.children[0], + sip = vip.m, + sop = vop.m, + sim = vim.m, + som = vom.m, + shift; + while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) { + vom = nextLeft(vom); + vop = nextRight(vop); + vop.a = v; + shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); + if (shift > 0) { + moveSubtree(nextAncestor(vim, v, ancestor), v, shift); + sip += shift; + sop += shift; + } + sim += vim.m; + sip += vip.m; + som += vom.m; + sop += vop.m; + } + if (vim && !nextRight(vop)) { + vop.t = vim; + vop.m += sim - sop; + } + if (vip && !nextLeft(vom)) { + vom.t = vip; + vom.m += sip - som; + ancestor = v; + } + } + return ancestor; + } + + function sizeNode(node) { + node.x *= dx; + node.y = node.depth * dy; + } + + tree.separation = function(x) { + return arguments.length ? (separation = x, tree) : separation; + }; + + tree.size = function(x) { + return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]); + }; + + tree.nodeSize = function(x) { + return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null); + }; + + return tree; +} + +function treemapSlice(parent, x0, y0, x1, y1) { + var nodes = parent.children, + node, + i = -1, + n = nodes.length, + k = parent.value && (y1 - y0) / parent.value; + + while (++i < n) { + node = nodes[i], node.x0 = x0, node.x1 = x1; + node.y0 = y0, node.y1 = y0 += node.value * k; + } +} + +var phi = (1 + Math.sqrt(5)) / 2; + +function squarifyRatio(ratio, parent, x0, y0, x1, y1) { + var rows = [], + nodes = parent.children, + row, + nodeValue, + i0 = 0, + i1 = 0, + n = nodes.length, + dx, dy, + value = parent.value, + sumValue, + minValue, + maxValue, + newRatio, + minRatio, + alpha, + beta; + + while (i0 < n) { + dx = x1 - x0, dy = y1 - y0; + + // Find the next non-empty node. + do sumValue = nodes[i1++].value; while (!sumValue && i1 < n); + minValue = maxValue = sumValue; + alpha = Math.max(dy / dx, dx / dy) / (value * ratio); + beta = sumValue * sumValue * alpha; + minRatio = Math.max(maxValue / beta, beta / minValue); + + // Keep adding nodes while the aspect ratio maintains or improves. + for (; i1 < n; ++i1) { + sumValue += nodeValue = nodes[i1].value; + if (nodeValue < minValue) minValue = nodeValue; + if (nodeValue > maxValue) maxValue = nodeValue; + beta = sumValue * sumValue * alpha; + newRatio = Math.max(maxValue / beta, beta / minValue); + if (newRatio > minRatio) { sumValue -= nodeValue; break; } + minRatio = newRatio; + } + + // Position and record the row orientation. + rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)}); + if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1); + else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1); + value -= sumValue, i0 = i1; + } + + return rows; +} + +var squarify = (function custom(ratio) { + + function squarify(parent, x0, y0, x1, y1) { + squarifyRatio(ratio, parent, x0, y0, x1, y1); + } + + squarify.ratio = function(x) { + return custom((x = +x) > 1 ? x : 1); + }; + + return squarify; +})(phi); + +function index$3() { + var tile = squarify, + round = false, + dx = 1, + dy = 1, + paddingStack = [0], + paddingInner = constantZero, + paddingTop = constantZero, + paddingRight = constantZero, + paddingBottom = constantZero, + paddingLeft = constantZero; + + function treemap(root) { + root.x0 = + root.y0 = 0; + root.x1 = dx; + root.y1 = dy; + root.eachBefore(positionNode); + paddingStack = [0]; + if (round) root.eachBefore(roundNode); + return root; + } + + function positionNode(node) { + var p = paddingStack[node.depth], + x0 = node.x0 + p, + y0 = node.y0 + p, + x1 = node.x1 - p, + y1 = node.y1 - p; + if (x1 < x0) x0 = x1 = (x0 + x1) / 2; + if (y1 < y0) y0 = y1 = (y0 + y1) / 2; + node.x0 = x0; + node.y0 = y0; + node.x1 = x1; + node.y1 = y1; + if (node.children) { + p = paddingStack[node.depth + 1] = paddingInner(node) / 2; + x0 += paddingLeft(node) - p; + y0 += paddingTop(node) - p; + x1 -= paddingRight(node) - p; + y1 -= paddingBottom(node) - p; + if (x1 < x0) x0 = x1 = (x0 + x1) / 2; + if (y1 < y0) y0 = y1 = (y0 + y1) / 2; + tile(node, x0, y0, x1, y1); + } + } + + treemap.round = function(x) { + return arguments.length ? (round = !!x, treemap) : round; + }; + + treemap.size = function(x) { + return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy]; + }; + + treemap.tile = function(x) { + return arguments.length ? (tile = required(x), treemap) : tile; + }; + + treemap.padding = function(x) { + return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner(); + }; + + treemap.paddingInner = function(x) { + return arguments.length ? (paddingInner = typeof x === "function" ? x : constant$8(+x), treemap) : paddingInner; + }; + + treemap.paddingOuter = function(x) { + return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop(); + }; + + treemap.paddingTop = function(x) { + return arguments.length ? (paddingTop = typeof x === "function" ? x : constant$8(+x), treemap) : paddingTop; + }; + + treemap.paddingRight = function(x) { + return arguments.length ? (paddingRight = typeof x === "function" ? x : constant$8(+x), treemap) : paddingRight; + }; + + treemap.paddingBottom = function(x) { + return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant$8(+x), treemap) : paddingBottom; + }; + + treemap.paddingLeft = function(x) { + return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant$8(+x), treemap) : paddingLeft; + }; + + return treemap; +} + +function binary(parent, x0, y0, x1, y1) { + var nodes = parent.children, + i, n = nodes.length, + sum, sums = new Array(n + 1); + + for (sums[0] = sum = i = 0; i < n; ++i) { + sums[i + 1] = sum += nodes[i].value; + } + + partition(0, n, parent.value, x0, y0, x1, y1); + + function partition(i, j, value, x0, y0, x1, y1) { + if (i >= j - 1) { + var node = nodes[i]; + node.x0 = x0, node.y0 = y0; + node.x1 = x1, node.y1 = y1; + return; + } + + var valueOffset = sums[i], + valueTarget = (value / 2) + valueOffset, + k = i + 1, + hi = j - 1; + + while (k < hi) { + var mid = k + hi >>> 1; + if (sums[mid] < valueTarget) k = mid + 1; + else hi = mid; + } + + if ((valueTarget - sums[k - 1]) < (sums[k] - valueTarget) && i + 1 < k) --k; + + var valueLeft = sums[k] - valueOffset, + valueRight = value - valueLeft; + + if ((x1 - x0) > (y1 - y0)) { + var xk = (x0 * valueRight + x1 * valueLeft) / value; + partition(i, k, valueLeft, x0, y0, xk, y1); + partition(k, j, valueRight, xk, y0, x1, y1); + } else { + var yk = (y0 * valueRight + y1 * valueLeft) / value; + partition(i, k, valueLeft, x0, y0, x1, yk); + partition(k, j, valueRight, x0, yk, x1, y1); + } + } +} + +function sliceDice(parent, x0, y0, x1, y1) { + (parent.depth & 1 ? treemapSlice : treemapDice)(parent, x0, y0, x1, y1); +} + +var resquarify = (function custom(ratio) { + + function resquarify(parent, x0, y0, x1, y1) { + if ((rows = parent._squarify) && (rows.ratio === ratio)) { + var rows, + row, + nodes, + i, + j = -1, + n, + m = rows.length, + value = parent.value; + + while (++j < m) { + row = rows[j], nodes = row.children; + for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value; + if (row.dice) treemapDice(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value); + else treemapSlice(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1); + value -= row.value; + } + } else { + parent._squarify = rows = squarifyRatio(ratio, parent, x0, y0, x1, y1); + rows.ratio = ratio; + } + } + + resquarify.ratio = function(x) { + return custom((x = +x) > 1 ? x : 1); + }; + + return resquarify; +})(phi); + +function area$1(polygon) { + var i = -1, + n = polygon.length, + a, + b = polygon[n - 1], + area = 0; + + while (++i < n) { + a = b; + b = polygon[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + + return area / 2; +} + +function centroid$1(polygon) { + var i = -1, + n = polygon.length, + x = 0, + y = 0, + a, + b = polygon[n - 1], + c, + k = 0; + + while (++i < n) { + a = b; + b = polygon[i]; + k += c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + + return k *= 3, [x / k, y / k]; +} + +// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of +// the 3D cross product in a quadrant I Cartesian coordinate system (+x is +// right, +y is up). Returns a positive value if ABC is counter-clockwise, +// negative if clockwise, and zero if the points are collinear. +function cross$1(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); +} + +function lexicographicOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; +} + +// Computes the upper convex hull per the monotone chain algorithm. +// Assumes points.length >= 3, is sorted by x, unique in y. +// Returns an array of indices into points in left-to-right order. +function computeUpperHullIndexes(points) { + var n = points.length, + indexes = [0, 1], + size = 2; + + for (var i = 2; i < n; ++i) { + while (size > 1 && cross$1(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size; + indexes[size++] = i; + } + + return indexes.slice(0, size); // remove popped points +} + +function hull(points) { + if ((n = points.length) < 3) return null; + + var i, + n, + sortedPoints = new Array(n), + flippedPoints = new Array(n); + + for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i]; + sortedPoints.sort(lexicographicOrder); + for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]]; + + var upperIndexes = computeUpperHullIndexes(sortedPoints), + lowerIndexes = computeUpperHullIndexes(flippedPoints); + + // Construct the hull polygon, removing possible duplicate endpoints. + var skipLeft = lowerIndexes[0] === upperIndexes[0], + skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1], + hull = []; + + // Add upper hull in right-to-l order. + // Then add lower hull in left-to-right order. + for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]); + for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]); + + return hull; +} + +function contains$1(polygon, point) { + var n = polygon.length, + p = polygon[n - 1], + x = point[0], y = point[1], + x0 = p[0], y0 = p[1], + x1, y1, + inside = false; + + for (var i = 0; i < n; ++i) { + p = polygon[i], x1 = p[0], y1 = p[1]; + if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside; + x0 = x1, y0 = y1; + } + + return inside; +} + +function length$2(polygon) { + var i = -1, + n = polygon.length, + b = polygon[n - 1], + xa, + ya, + xb = b[0], + yb = b[1], + perimeter = 0; + + while (++i < n) { + xa = xb; + ya = yb; + b = polygon[i]; + xb = b[0]; + yb = b[1]; + xa -= xb; + ya -= yb; + perimeter += Math.sqrt(xa * xa + ya * ya); + } + + return perimeter; +} + +var slice$4 = [].slice; + +var noabort = {}; + +function Queue(size) { + this._size = size; + this._call = + this._error = null; + this._tasks = []; + this._data = []; + this._waiting = + this._active = + this._ended = + this._start = 0; // inside a synchronous task callback? +} + +Queue.prototype = queue.prototype = { + constructor: Queue, + defer: function(callback) { + if (typeof callback !== "function") throw new Error("invalid callback"); + if (this._call) throw new Error("defer after await"); + if (this._error != null) return this; + var t = slice$4.call(arguments, 1); + t.push(callback); + ++this._waiting, this._tasks.push(t); + poke$1(this); + return this; + }, + abort: function() { + if (this._error == null) abort(this, new Error("abort")); + return this; + }, + await: function(callback) { + if (typeof callback !== "function") throw new Error("invalid callback"); + if (this._call) throw new Error("multiple await"); + this._call = function(error, results) { callback.apply(null, [error].concat(results)); }; + maybeNotify(this); + return this; + }, + awaitAll: function(callback) { + if (typeof callback !== "function") throw new Error("invalid callback"); + if (this._call) throw new Error("multiple await"); + this._call = callback; + maybeNotify(this); + return this; + } +}; + +function poke$1(q) { + if (!q._start) { + try { start$1(q); } // let the current task complete + catch (e) { + if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously + else if (!q._data) throw e; // await callback errored synchronously + } + } +} + +function start$1(q) { + while (q._start = q._waiting && q._active < q._size) { + var i = q._ended + q._active, + t = q._tasks[i], + j = t.length - 1, + c = t[j]; + t[j] = end(q, i); + --q._waiting, ++q._active; + t = c.apply(null, t); + if (!q._tasks[i]) continue; // task finished synchronously + q._tasks[i] = t || noabort; + } +} + +function end(q, i) { + return function(e, r) { + if (!q._tasks[i]) return; // ignore multiple callbacks + --q._active, ++q._ended; + q._tasks[i] = null; + if (q._error != null) return; // ignore secondary errors + if (e != null) { + abort(q, e); + } else { + q._data[i] = r; + if (q._waiting) poke$1(q); + else maybeNotify(q); + } + }; +} + +function abort(q, e) { + var i = q._tasks.length, t; + q._error = e; // ignore active callbacks + q._data = undefined; // allow gc + q._waiting = NaN; // prevent starting + + while (--i >= 0) { + if (t = q._tasks[i]) { + q._tasks[i] = null; + if (t.abort) { + try { t.abort(); } + catch (e) { /* ignore */ } + } + } + } + + q._active = NaN; // allow notification + maybeNotify(q); +} + +function maybeNotify(q) { + if (!q._active && q._call) { + var d = q._data; + q._data = undefined; // allow gc + q._call(q._error, d); + } +} + +function queue(concurrency) { + if (concurrency == null) concurrency = Infinity; + else if (!((concurrency = +concurrency) >= 1)) throw new Error("invalid concurrency"); + return new Queue(concurrency); +} + +function defaultSource$1() { + return Math.random(); +} + +var uniform = (function sourceRandomUniform(source) { + function randomUniform(min, max) { + min = min == null ? 0 : +min; + max = max == null ? 1 : +max; + if (arguments.length === 1) max = min, min = 0; + else max -= min; + return function() { + return source() * max + min; + }; + } + + randomUniform.source = sourceRandomUniform; + + return randomUniform; +})(defaultSource$1); + +var normal = (function sourceRandomNormal(source) { + function randomNormal(mu, sigma) { + var x, r; + mu = mu == null ? 0 : +mu; + sigma = sigma == null ? 1 : +sigma; + return function() { + var y; + + // If available, use the second previously-generated uniform random. + if (x != null) y = x, x = null; + + // Otherwise, generate a new x and y. + else do { + x = source() * 2 - 1; + y = source() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + + return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r); + }; + } + + randomNormal.source = sourceRandomNormal; + + return randomNormal; +})(defaultSource$1); + +var logNormal = (function sourceRandomLogNormal(source) { + function randomLogNormal() { + var randomNormal = normal.source(source).apply(this, arguments); + return function() { + return Math.exp(randomNormal()); + }; + } + + randomLogNormal.source = sourceRandomLogNormal; + + return randomLogNormal; +})(defaultSource$1); + +var irwinHall = (function sourceRandomIrwinHall(source) { + function randomIrwinHall(n) { + return function() { + for (var sum = 0, i = 0; i < n; ++i) sum += source(); + return sum; + }; + } + + randomIrwinHall.source = sourceRandomIrwinHall; + + return randomIrwinHall; +})(defaultSource$1); + +var bates = (function sourceRandomBates(source) { + function randomBates(n) { + var randomIrwinHall = irwinHall.source(source)(n); + return function() { + return randomIrwinHall() / n; + }; + } + + randomBates.source = sourceRandomBates; + + return randomBates; +})(defaultSource$1); + +var exponential$1 = (function sourceRandomExponential(source) { + function randomExponential(lambda) { + return function() { + return -Math.log(1 - source()) / lambda; + }; + } + + randomExponential.source = sourceRandomExponential; + + return randomExponential; +})(defaultSource$1); + +function request(url, callback) { + var request, + event = dispatch("beforesend", "progress", "load", "error"), + mimeType, + headers = map$1(), + xhr = new XMLHttpRequest, + user = null, + password = null, + response, + responseType, + timeout = 0; + + // If IE does not support CORS, use XDomainRequest. + if (typeof XDomainRequest !== "undefined" + && !("withCredentials" in xhr) + && /^(http(s)?:)?\/\//.test(url)) xhr = new XDomainRequest; + + "onload" in xhr + ? xhr.onload = xhr.onerror = xhr.ontimeout = respond + : xhr.onreadystatechange = function(o) { xhr.readyState > 3 && respond(o); }; + + function respond(o) { + var status = xhr.status, result; + if (!status && hasResponse(xhr) + || status >= 200 && status < 300 + || status === 304) { + if (response) { + try { + result = response.call(request, xhr); + } catch (e) { + event.call("error", request, e); + return; + } + } else { + result = xhr; + } + event.call("load", request, result); + } else { + event.call("error", request, o); + } + } + + xhr.onprogress = function(e) { + event.call("progress", request, e); + }; + + request = { + header: function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers.get(name); + if (value == null) headers.remove(name); + else headers.set(name, value + ""); + return request; + }, + + // If mimeType is non-null and no Accept header is set, a default is used. + mimeType: function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return request; + }, + + // Specifies what type the response value should take; + // for instance, arraybuffer, blob, document, or text. + responseType: function(value) { + if (!arguments.length) return responseType; + responseType = value; + return request; + }, + + timeout: function(value) { + if (!arguments.length) return timeout; + timeout = +value; + return request; + }, + + user: function(value) { + return arguments.length < 1 ? user : (user = value == null ? null : value + "", request); + }, + + password: function(value) { + return arguments.length < 1 ? password : (password = value == null ? null : value + "", request); + }, + + // Specify how to convert the response content to a specific type; + // changes the callback value on "load" events. + response: function(value) { + response = value; + return request; + }, + + // Alias for send("GET", …). + get: function(data, callback) { + return request.send("GET", data, callback); + }, + + // Alias for send("POST", …). + post: function(data, callback) { + return request.send("POST", data, callback); + }, + + // If callback is non-null, it will be used for error and load events. + send: function(method, data, callback) { + xhr.open(method, url, true, user, password); + if (mimeType != null && !headers.has("accept")) headers.set("accept", mimeType + ",*/*"); + if (xhr.setRequestHeader) headers.each(function(value, name) { xhr.setRequestHeader(name, value); }); + if (mimeType != null && xhr.overrideMimeType) xhr.overrideMimeType(mimeType); + if (responseType != null) xhr.responseType = responseType; + if (timeout > 0) xhr.timeout = timeout; + if (callback == null && typeof data === "function") callback = data, data = null; + if (callback != null && callback.length === 1) callback = fixCallback(callback); + if (callback != null) request.on("error", callback).on("load", function(xhr) { callback(null, xhr); }); + event.call("beforesend", request, xhr); + xhr.send(data == null ? null : data); + return request; + }, + + abort: function() { + xhr.abort(); + return request; + }, + + on: function() { + var value = event.on.apply(event, arguments); + return value === event ? request : value; + } + }; + + if (callback != null) { + if (typeof callback !== "function") throw new Error("invalid callback: " + callback); + return request.get(callback); + } + + return request; +} + +function fixCallback(callback) { + return function(error, xhr) { + callback(error == null ? xhr : null); + }; +} + +function hasResponse(xhr) { + var type = xhr.responseType; + return type && type !== "text" + ? xhr.response // null on error + : xhr.responseText; // "" on error +} + +function type$1(defaultMimeType, response) { + return function(url, callback) { + var r = request(url).mimeType(defaultMimeType).response(response); + if (callback != null) { + if (typeof callback !== "function") throw new Error("invalid callback: " + callback); + return r.get(callback); + } + return r; + }; +} + +var html = type$1("text/html", function(xhr) { + return document.createRange().createContextualFragment(xhr.responseText); +}); + +var json = type$1("application/json", function(xhr) { + return JSON.parse(xhr.responseText); +}); + +var text = type$1("text/plain", function(xhr) { + return xhr.responseText; +}); + +var xml = type$1("application/xml", function(xhr) { + var xml = xhr.responseXML; + if (!xml) throw new Error("parse error"); + return xml; +}); + +function dsv$1(defaultMimeType, parse) { + return function(url, row, callback) { + if (arguments.length < 3) callback = row, row = null; + var r = request(url).mimeType(defaultMimeType); + r.row = function(_) { return arguments.length ? r.response(responseOf(parse, row = _)) : row; }; + r.row(row); + return callback ? r.get(callback) : r; + }; +} + +function responseOf(parse, row) { + return function(request$$1) { + return parse(request$$1.responseText, row); + }; +} + +var csv$1 = dsv$1("text/csv", csvParse); + +var tsv$1 = dsv$1("text/tab-separated-values", tsvParse); + +var array$2 = Array.prototype; + +var map$3 = array$2.map; +var slice$5 = array$2.slice; + +var implicit = {name: "implicit"}; + +function ordinal(range) { + var index = map$1(), + domain = [], + unknown = implicit; + + range = range == null ? [] : slice$5.call(range); + + function scale(d) { + var key = d + "", i = index.get(key); + if (!i) { + if (unknown !== implicit) return unknown; + index.set(key, i = domain.push(d)); + } + return range[(i - 1) % range.length]; + } + + scale.domain = function(_) { + if (!arguments.length) return domain.slice(); + domain = [], index = map$1(); + var i = -1, n = _.length, d, key; + while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d)); + return scale; + }; + + scale.range = function(_) { + return arguments.length ? (range = slice$5.call(_), scale) : range.slice(); + }; + + scale.unknown = function(_) { + return arguments.length ? (unknown = _, scale) : unknown; + }; + + scale.copy = function() { + return ordinal() + .domain(domain) + .range(range) + .unknown(unknown); + }; + + return scale; +} + +function band() { + var scale = ordinal().unknown(undefined), + domain = scale.domain, + ordinalRange = scale.range, + range$$1 = [0, 1], + step, + bandwidth, + round = false, + paddingInner = 0, + paddingOuter = 0, + align = 0.5; + + delete scale.unknown; + + function rescale() { + var n = domain().length, + reverse = range$$1[1] < range$$1[0], + start = range$$1[reverse - 0], + stop = range$$1[1 - reverse]; + step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2); + if (round) step = Math.floor(step); + start += (stop - start - step * (n - paddingInner)) * align; + bandwidth = step * (1 - paddingInner); + if (round) start = Math.round(start), bandwidth = Math.round(bandwidth); + var values = sequence(n).map(function(i) { return start + step * i; }); + return ordinalRange(reverse ? values.reverse() : values); + } + + scale.domain = function(_) { + return arguments.length ? (domain(_), rescale()) : domain(); + }; + + scale.range = function(_) { + return arguments.length ? (range$$1 = [+_[0], +_[1]], rescale()) : range$$1.slice(); + }; + + scale.rangeRound = function(_) { + return range$$1 = [+_[0], +_[1]], round = true, rescale(); + }; + + scale.bandwidth = function() { + return bandwidth; + }; + + scale.step = function() { + return step; + }; + + scale.round = function(_) { + return arguments.length ? (round = !!_, rescale()) : round; + }; + + scale.padding = function(_) { + return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner; + }; + + scale.paddingInner = function(_) { + return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner; + }; + + scale.paddingOuter = function(_) { + return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter; + }; + + scale.align = function(_) { + return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align; + }; + + scale.copy = function() { + return band() + .domain(domain()) + .range(range$$1) + .round(round) + .paddingInner(paddingInner) + .paddingOuter(paddingOuter) + .align(align); + }; + + return rescale(); +} + +function pointish(scale) { + var copy = scale.copy; + + scale.padding = scale.paddingOuter; + delete scale.paddingInner; + delete scale.paddingOuter; + + scale.copy = function() { + return pointish(copy()); + }; + + return scale; +} + +function point$1() { + return pointish(band().paddingInner(1)); +} + +function constant$9(x) { + return function() { + return x; + }; +} + +function number$2(x) { + return +x; +} + +var unit = [0, 1]; + +function deinterpolateLinear(a, b) { + return (b -= (a = +a)) + ? function(x) { return (x - a) / b; } + : constant$9(b); +} + +function deinterpolateClamp(deinterpolate) { + return function(a, b) { + var d = deinterpolate(a = +a, b = +b); + return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); }; + }; +} + +function reinterpolateClamp(reinterpolate) { + return function(a, b) { + var r = reinterpolate(a = +a, b = +b); + return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); }; + }; +} + +function bimap(domain, range, deinterpolate, reinterpolate) { + var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1]; + if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0); + else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1); + return function(x) { return r0(d0(x)); }; +} + +function polymap(domain, range, deinterpolate, reinterpolate) { + var j = Math.min(domain.length, range.length) - 1, + d = new Array(j), + r = new Array(j), + i = -1; + + // Reverse descending domains. + if (domain[j] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + + while (++i < j) { + d[i] = deinterpolate(domain[i], domain[i + 1]); + r[i] = reinterpolate(range[i], range[i + 1]); + } + + return function(x) { + var i = bisectRight(domain, x, 1, j) - 1; + return r[i](d[i](x)); + }; +} + +function copy(source, target) { + return target + .domain(source.domain()) + .range(source.range()) + .interpolate(source.interpolate()) + .clamp(source.clamp()); +} + +// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1]. +// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b]. +function continuous(deinterpolate, reinterpolate) { + var domain = unit, + range = unit, + interpolate$$1 = interpolateValue, + clamp = false, + piecewise, + output, + input; + + function rescale() { + piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap; + output = input = null; + return scale; + } + + function scale(x) { + return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate$$1)))(+x); + } + + scale.invert = function(y) { + return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y); + }; + + scale.domain = function(_) { + return arguments.length ? (domain = map$3.call(_, number$2), rescale()) : domain.slice(); + }; + + scale.range = function(_) { + return arguments.length ? (range = slice$5.call(_), rescale()) : range.slice(); + }; + + scale.rangeRound = function(_) { + return range = slice$5.call(_), interpolate$$1 = interpolateRound, rescale(); + }; + + scale.clamp = function(_) { + return arguments.length ? (clamp = !!_, rescale()) : clamp; + }; + + scale.interpolate = function(_) { + return arguments.length ? (interpolate$$1 = _, rescale()) : interpolate$$1; + }; + + return rescale(); +} + +function tickFormat(domain, count, specifier) { + var start = domain[0], + stop = domain[domain.length - 1], + step = tickStep(start, stop, count == null ? 10 : count), + precision; + specifier = formatSpecifier(specifier == null ? ",f" : specifier); + switch (specifier.type) { + case "s": { + var value = Math.max(Math.abs(start), Math.abs(stop)); + if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision; + return exports.formatPrefix(specifier, value); + } + case "": + case "e": + case "g": + case "p": + case "r": { + if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e"); + break; + } + case "f": + case "%": { + if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2; + break; + } + } + return exports.format(specifier); +} + +function linearish(scale) { + var domain = scale.domain; + + scale.ticks = function(count) { + var d = domain(); + return ticks(d[0], d[d.length - 1], count == null ? 10 : count); + }; + + scale.tickFormat = function(count, specifier) { + return tickFormat(domain(), count, specifier); + }; + + scale.nice = function(count) { + if (count == null) count = 10; + + var d = domain(), + i0 = 0, + i1 = d.length - 1, + start = d[i0], + stop = d[i1], + step; + + if (stop < start) { + step = start, start = stop, stop = step; + step = i0, i0 = i1, i1 = step; + } + + step = tickIncrement(start, stop, count); + + if (step > 0) { + start = Math.floor(start / step) * step; + stop = Math.ceil(stop / step) * step; + step = tickIncrement(start, stop, count); + } else if (step < 0) { + start = Math.ceil(start * step) / step; + stop = Math.floor(stop * step) / step; + step = tickIncrement(start, stop, count); + } + + if (step > 0) { + d[i0] = Math.floor(start / step) * step; + d[i1] = Math.ceil(stop / step) * step; + domain(d); + } else if (step < 0) { + d[i0] = Math.ceil(start * step) / step; + d[i1] = Math.floor(stop * step) / step; + domain(d); + } + + return scale; + }; + + return scale; +} + +function linear$2() { + var scale = continuous(deinterpolateLinear, reinterpolate); + + scale.copy = function() { + return copy(scale, linear$2()); + }; + + return linearish(scale); +} + +function identity$6() { + var domain = [0, 1]; + + function scale(x) { + return +x; + } + + scale.invert = scale; + + scale.domain = scale.range = function(_) { + return arguments.length ? (domain = map$3.call(_, number$2), scale) : domain.slice(); + }; + + scale.copy = function() { + return identity$6().domain(domain); + }; + + return linearish(scale); +} + +function nice(domain, interval) { + domain = domain.slice(); + + var i0 = 0, + i1 = domain.length - 1, + x0 = domain[i0], + x1 = domain[i1], + t; + + if (x1 < x0) { + t = i0, i0 = i1, i1 = t; + t = x0, x0 = x1, x1 = t; + } + + domain[i0] = interval.floor(x0); + domain[i1] = interval.ceil(x1); + return domain; +} + +function deinterpolate(a, b) { + return (b = Math.log(b / a)) + ? function(x) { return Math.log(x / a) / b; } + : constant$9(b); +} + +function reinterpolate$1(a, b) { + return a < 0 + ? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); } + : function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); }; +} + +function pow10(x) { + return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x; +} + +function powp(base) { + return base === 10 ? pow10 + : base === Math.E ? Math.exp + : function(x) { return Math.pow(base, x); }; +} + +function logp(base) { + return base === Math.E ? Math.log + : base === 10 && Math.log10 + || base === 2 && Math.log2 + || (base = Math.log(base), function(x) { return Math.log(x) / base; }); +} + +function reflect(f) { + return function(x) { + return -f(-x); + }; +} + +function log$1() { + var scale = continuous(deinterpolate, reinterpolate$1).domain([1, 10]), + domain = scale.domain, + base = 10, + logs = logp(10), + pows = powp(10); + + function rescale() { + logs = logp(base), pows = powp(base); + if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows); + return scale; + } + + scale.base = function(_) { + return arguments.length ? (base = +_, rescale()) : base; + }; + + scale.domain = function(_) { + return arguments.length ? (domain(_), rescale()) : domain(); + }; + + scale.ticks = function(count) { + var d = domain(), + u = d[0], + v = d[d.length - 1], + r; + + if (r = v < u) i = u, u = v, v = i; + + var i = logs(u), + j = logs(v), + p, + k, + t, + n = count == null ? 10 : +count, + z = []; + + if (!(base % 1) && j - i < n) { + i = Math.round(i) - 1, j = Math.round(j) + 1; + if (u > 0) for (; i < j; ++i) { + for (k = 1, p = pows(i); k < base; ++k) { + t = p * k; + if (t < u) continue; + if (t > v) break; + z.push(t); + } + } else for (; i < j; ++i) { + for (k = base - 1, p = pows(i); k >= 1; --k) { + t = p * k; + if (t < u) continue; + if (t > v) break; + z.push(t); + } + } + } else { + z = ticks(i, j, Math.min(j - i, n)).map(pows); + } + + return r ? z.reverse() : z; + }; + + scale.tickFormat = function(count, specifier) { + if (specifier == null) specifier = base === 10 ? ".0e" : ","; + if (typeof specifier !== "function") specifier = exports.format(specifier); + if (count === Infinity) return specifier; + if (count == null) count = 10; + var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate? + return function(d) { + var i = d / pows(Math.round(logs(d))); + if (i * base < base - 0.5) i *= base; + return i <= k ? specifier(d) : ""; + }; + }; + + scale.nice = function() { + return domain(nice(domain(), { + floor: function(x) { return pows(Math.floor(logs(x))); }, + ceil: function(x) { return pows(Math.ceil(logs(x))); } + })); + }; + + scale.copy = function() { + return copy(scale, log$1().base(base)); + }; + + return scale; +} + +function raise$1(x, exponent) { + return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent); +} + +function pow$1() { + var exponent = 1, + scale = continuous(deinterpolate, reinterpolate), + domain = scale.domain; + + function deinterpolate(a, b) { + return (b = raise$1(b, exponent) - (a = raise$1(a, exponent))) + ? function(x) { return (raise$1(x, exponent) - a) / b; } + : constant$9(b); + } + + function reinterpolate(a, b) { + b = raise$1(b, exponent) - (a = raise$1(a, exponent)); + return function(t) { return raise$1(a + b * t, 1 / exponent); }; + } + + scale.exponent = function(_) { + return arguments.length ? (exponent = +_, domain(domain())) : exponent; + }; + + scale.copy = function() { + return copy(scale, pow$1().exponent(exponent)); + }; + + return linearish(scale); +} + +function sqrt$1() { + return pow$1().exponent(0.5); +} + +function quantile$$1() { + var domain = [], + range = [], + thresholds = []; + + function rescale() { + var i = 0, n = Math.max(1, range.length); + thresholds = new Array(n - 1); + while (++i < n) thresholds[i - 1] = threshold(domain, i / n); + return scale; + } + + function scale(x) { + if (!isNaN(x = +x)) return range[bisectRight(thresholds, x)]; + } + + scale.invertExtent = function(y) { + var i = range.indexOf(y); + return i < 0 ? [NaN, NaN] : [ + i > 0 ? thresholds[i - 1] : domain[0], + i < thresholds.length ? thresholds[i] : domain[domain.length - 1] + ]; + }; + + scale.domain = function(_) { + if (!arguments.length) return domain.slice(); + domain = []; + for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d); + domain.sort(ascending); + return rescale(); + }; + + scale.range = function(_) { + return arguments.length ? (range = slice$5.call(_), rescale()) : range.slice(); + }; + + scale.quantiles = function() { + return thresholds.slice(); + }; + + scale.copy = function() { + return quantile$$1() + .domain(domain) + .range(range); + }; + + return scale; +} + +function quantize$1() { + var x0 = 0, + x1 = 1, + n = 1, + domain = [0.5], + range = [0, 1]; + + function scale(x) { + if (x <= x) return range[bisectRight(domain, x, 0, n)]; + } + + function rescale() { + var i = -1; + domain = new Array(n); + while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); + return scale; + } + + scale.domain = function(_) { + return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1]; + }; + + scale.range = function(_) { + return arguments.length ? (n = (range = slice$5.call(_)).length - 1, rescale()) : range.slice(); + }; + + scale.invertExtent = function(y) { + var i = range.indexOf(y); + return i < 0 ? [NaN, NaN] + : i < 1 ? [x0, domain[0]] + : i >= n ? [domain[n - 1], x1] + : [domain[i - 1], domain[i]]; + }; + + scale.copy = function() { + return quantize$1() + .domain([x0, x1]) + .range(range); + }; + + return linearish(scale); +} + +function threshold$1() { + var domain = [0.5], + range = [0, 1], + n = 1; + + function scale(x) { + if (x <= x) return range[bisectRight(domain, x, 0, n)]; + } + + scale.domain = function(_) { + return arguments.length ? (domain = slice$5.call(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice(); + }; + + scale.range = function(_) { + return arguments.length ? (range = slice$5.call(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice(); + }; + + scale.invertExtent = function(y) { + var i = range.indexOf(y); + return [domain[i - 1], domain[i]]; + }; + + scale.copy = function() { + return threshold$1() + .domain(domain) + .range(range); + }; + + return scale; +} + +var t0$1 = new Date; +var t1$1 = new Date; + +function newInterval(floori, offseti, count, field) { + + function interval(date) { + return floori(date = new Date(+date)), date; + } + + interval.floor = interval; + + interval.ceil = function(date) { + return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date; + }; + + interval.round = function(date) { + var d0 = interval(date), + d1 = interval.ceil(date); + return date - d0 < d1 - date ? d0 : d1; + }; + + interval.offset = function(date, step) { + return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date; + }; + + interval.range = function(start, stop, step) { + var range = [], previous; + start = interval.ceil(start); + step = step == null ? 1 : Math.floor(step); + if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date + do range.push(previous = new Date(+start)), offseti(start, step), floori(start); + while (previous < start && start < stop); + return range; + }; + + interval.filter = function(test) { + return newInterval(function(date) { + if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1); + }, function(date, step) { + if (date >= date) { + if (step < 0) while (++step <= 0) { + while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty + } else while (--step >= 0) { + while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty + } + } + }); + }; + + if (count) { + interval.count = function(start, end) { + t0$1.setTime(+start), t1$1.setTime(+end); + floori(t0$1), floori(t1$1); + return Math.floor(count(t0$1, t1$1)); + }; + + interval.every = function(step) { + step = Math.floor(step); + return !isFinite(step) || !(step > 0) ? null + : !(step > 1) ? interval + : interval.filter(field + ? function(d) { return field(d) % step === 0; } + : function(d) { return interval.count(0, d) % step === 0; }); + }; + } + + return interval; +} + +var millisecond = newInterval(function() { + // noop +}, function(date, step) { + date.setTime(+date + step); +}, function(start, end) { + return end - start; +}); + +// An optimized implementation for this simple case. +millisecond.every = function(k) { + k = Math.floor(k); + if (!isFinite(k) || !(k > 0)) return null; + if (!(k > 1)) return millisecond; + return newInterval(function(date) { + date.setTime(Math.floor(date / k) * k); + }, function(date, step) { + date.setTime(+date + step * k); + }, function(start, end) { + return (end - start) / k; + }); +}; + +var milliseconds = millisecond.range; + +var durationSecond$1 = 1e3; +var durationMinute$1 = 6e4; +var durationHour$1 = 36e5; +var durationDay$1 = 864e5; +var durationWeek$1 = 6048e5; + +var second = newInterval(function(date) { + date.setTime(Math.floor(date / durationSecond$1) * durationSecond$1); +}, function(date, step) { + date.setTime(+date + step * durationSecond$1); +}, function(start, end) { + return (end - start) / durationSecond$1; +}, function(date) { + return date.getUTCSeconds(); +}); + +var seconds = second.range; + +var minute = newInterval(function(date) { + date.setTime(Math.floor(date / durationMinute$1) * durationMinute$1); +}, function(date, step) { + date.setTime(+date + step * durationMinute$1); +}, function(start, end) { + return (end - start) / durationMinute$1; +}, function(date) { + return date.getMinutes(); +}); + +var minutes = minute.range; + +var hour = newInterval(function(date) { + var offset = date.getTimezoneOffset() * durationMinute$1 % durationHour$1; + if (offset < 0) offset += durationHour$1; + date.setTime(Math.floor((+date - offset) / durationHour$1) * durationHour$1 + offset); +}, function(date, step) { + date.setTime(+date + step * durationHour$1); +}, function(start, end) { + return (end - start) / durationHour$1; +}, function(date) { + return date.getHours(); +}); + +var hours = hour.range; + +var day = newInterval(function(date) { + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setDate(date.getDate() + step); +}, function(start, end) { + return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationDay$1; +}, function(date) { + return date.getDate() - 1; +}); + +var days = day.range; + +function weekday(i) { + return newInterval(function(date) { + date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); + date.setHours(0, 0, 0, 0); + }, function(date, step) { + date.setDate(date.getDate() + step * 7); + }, function(start, end) { + return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute$1) / durationWeek$1; + }); +} + +var sunday = weekday(0); +var monday = weekday(1); +var tuesday = weekday(2); +var wednesday = weekday(3); +var thursday = weekday(4); +var friday = weekday(5); +var saturday = weekday(6); + +var sundays = sunday.range; +var mondays = monday.range; +var tuesdays = tuesday.range; +var wednesdays = wednesday.range; +var thursdays = thursday.range; +var fridays = friday.range; +var saturdays = saturday.range; + +var month = newInterval(function(date) { + date.setDate(1); + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setMonth(date.getMonth() + step); +}, function(start, end) { + return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12; +}, function(date) { + return date.getMonth(); +}); + +var months = month.range; + +var year = newInterval(function(date) { + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); +}, function(date, step) { + date.setFullYear(date.getFullYear() + step); +}, function(start, end) { + return end.getFullYear() - start.getFullYear(); +}, function(date) { + return date.getFullYear(); +}); + +// An optimized implementation for this simple case. +year.every = function(k) { + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { + date.setFullYear(Math.floor(date.getFullYear() / k) * k); + date.setMonth(0, 1); + date.setHours(0, 0, 0, 0); + }, function(date, step) { + date.setFullYear(date.getFullYear() + step * k); + }); +}; + +var years = year.range; + +var utcMinute = newInterval(function(date) { + date.setUTCSeconds(0, 0); +}, function(date, step) { + date.setTime(+date + step * durationMinute$1); +}, function(start, end) { + return (end - start) / durationMinute$1; +}, function(date) { + return date.getUTCMinutes(); +}); + +var utcMinutes = utcMinute.range; + +var utcHour = newInterval(function(date) { + date.setUTCMinutes(0, 0, 0); +}, function(date, step) { + date.setTime(+date + step * durationHour$1); +}, function(start, end) { + return (end - start) / durationHour$1; +}, function(date) { + return date.getUTCHours(); +}); + +var utcHours = utcHour.range; + +var utcDay = newInterval(function(date) { + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCDate(date.getUTCDate() + step); +}, function(start, end) { + return (end - start) / durationDay$1; +}, function(date) { + return date.getUTCDate() - 1; +}); + +var utcDays = utcDay.range; + +function utcWeekday(i) { + return newInterval(function(date) { + date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); + date.setUTCHours(0, 0, 0, 0); + }, function(date, step) { + date.setUTCDate(date.getUTCDate() + step * 7); + }, function(start, end) { + return (end - start) / durationWeek$1; + }); +} + +var utcSunday = utcWeekday(0); +var utcMonday = utcWeekday(1); +var utcTuesday = utcWeekday(2); +var utcWednesday = utcWeekday(3); +var utcThursday = utcWeekday(4); +var utcFriday = utcWeekday(5); +var utcSaturday = utcWeekday(6); + +var utcSundays = utcSunday.range; +var utcMondays = utcMonday.range; +var utcTuesdays = utcTuesday.range; +var utcWednesdays = utcWednesday.range; +var utcThursdays = utcThursday.range; +var utcFridays = utcFriday.range; +var utcSaturdays = utcSaturday.range; + +var utcMonth = newInterval(function(date) { + date.setUTCDate(1); + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCMonth(date.getUTCMonth() + step); +}, function(start, end) { + return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; +}, function(date) { + return date.getUTCMonth(); +}); + +var utcMonths = utcMonth.range; + +var utcYear = newInterval(function(date) { + date.setUTCMonth(0, 1); + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCFullYear(date.getUTCFullYear() + step); +}, function(start, end) { + return end.getUTCFullYear() - start.getUTCFullYear(); +}, function(date) { + return date.getUTCFullYear(); +}); + +// An optimized implementation for this simple case. +utcYear.every = function(k) { + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) { + date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); + date.setUTCMonth(0, 1); + date.setUTCHours(0, 0, 0, 0); + }, function(date, step) { + date.setUTCFullYear(date.getUTCFullYear() + step * k); + }); +}; + +var utcYears = utcYear.range; + +function localDate(d) { + if (0 <= d.y && d.y < 100) { + var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L); + date.setFullYear(d.y); + return date; + } + return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L); +} + +function utcDate(d) { + if (0 <= d.y && d.y < 100) { + var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L)); + date.setUTCFullYear(d.y); + return date; + } + return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L)); +} + +function newYear(y) { + return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0}; +} + +function formatLocale$1(locale) { + var locale_dateTime = locale.dateTime, + locale_date = locale.date, + locale_time = locale.time, + locale_periods = locale.periods, + locale_weekdays = locale.days, + locale_shortWeekdays = locale.shortDays, + locale_months = locale.months, + locale_shortMonths = locale.shortMonths; + + var periodRe = formatRe(locale_periods), + periodLookup = formatLookup(locale_periods), + weekdayRe = formatRe(locale_weekdays), + weekdayLookup = formatLookup(locale_weekdays), + shortWeekdayRe = formatRe(locale_shortWeekdays), + shortWeekdayLookup = formatLookup(locale_shortWeekdays), + monthRe = formatRe(locale_months), + monthLookup = formatLookup(locale_months), + shortMonthRe = formatRe(locale_shortMonths), + shortMonthLookup = formatLookup(locale_shortMonths); + + var formats = { + "a": formatShortWeekday, + "A": formatWeekday, + "b": formatShortMonth, + "B": formatMonth, + "c": null, + "d": formatDayOfMonth, + "e": formatDayOfMonth, + "f": formatMicroseconds, + "H": formatHour24, + "I": formatHour12, + "j": formatDayOfYear, + "L": formatMilliseconds, + "m": formatMonthNumber, + "M": formatMinutes, + "p": formatPeriod, + "Q": formatUnixTimestamp, + "s": formatUnixTimestampSeconds, + "S": formatSeconds, + "u": formatWeekdayNumberMonday, + "U": formatWeekNumberSunday, + "V": formatWeekNumberISO, + "w": formatWeekdayNumberSunday, + "W": formatWeekNumberMonday, + "x": null, + "X": null, + "y": formatYear, + "Y": formatFullYear, + "Z": formatZone, + "%": formatLiteralPercent + }; + + var utcFormats = { + "a": formatUTCShortWeekday, + "A": formatUTCWeekday, + "b": formatUTCShortMonth, + "B": formatUTCMonth, + "c": null, + "d": formatUTCDayOfMonth, + "e": formatUTCDayOfMonth, + "f": formatUTCMicroseconds, + "H": formatUTCHour24, + "I": formatUTCHour12, + "j": formatUTCDayOfYear, + "L": formatUTCMilliseconds, + "m": formatUTCMonthNumber, + "M": formatUTCMinutes, + "p": formatUTCPeriod, + "Q": formatUnixTimestamp, + "s": formatUnixTimestampSeconds, + "S": formatUTCSeconds, + "u": formatUTCWeekdayNumberMonday, + "U": formatUTCWeekNumberSunday, + "V": formatUTCWeekNumberISO, + "w": formatUTCWeekdayNumberSunday, + "W": formatUTCWeekNumberMonday, + "x": null, + "X": null, + "y": formatUTCYear, + "Y": formatUTCFullYear, + "Z": formatUTCZone, + "%": formatLiteralPercent + }; + + var parses = { + "a": parseShortWeekday, + "A": parseWeekday, + "b": parseShortMonth, + "B": parseMonth, + "c": parseLocaleDateTime, + "d": parseDayOfMonth, + "e": parseDayOfMonth, + "f": parseMicroseconds, + "H": parseHour24, + "I": parseHour24, + "j": parseDayOfYear, + "L": parseMilliseconds, + "m": parseMonthNumber, + "M": parseMinutes, + "p": parsePeriod, + "Q": parseUnixTimestamp, + "s": parseUnixTimestampSeconds, + "S": parseSeconds, + "u": parseWeekdayNumberMonday, + "U": parseWeekNumberSunday, + "V": parseWeekNumberISO, + "w": parseWeekdayNumberSunday, + "W": parseWeekNumberMonday, + "x": parseLocaleDate, + "X": parseLocaleTime, + "y": parseYear, + "Y": parseFullYear, + "Z": parseZone, + "%": parseLiteralPercent + }; + + // These recursive directive definitions must be deferred. + formats.x = newFormat(locale_date, formats); + formats.X = newFormat(locale_time, formats); + formats.c = newFormat(locale_dateTime, formats); + utcFormats.x = newFormat(locale_date, utcFormats); + utcFormats.X = newFormat(locale_time, utcFormats); + utcFormats.c = newFormat(locale_dateTime, utcFormats); + + function newFormat(specifier, formats) { + return function(date) { + var string = [], + i = -1, + j = 0, + n = specifier.length, + c, + pad, + format; + + if (!(date instanceof Date)) date = new Date(+date); + + while (++i < n) { + if (specifier.charCodeAt(i) === 37) { + string.push(specifier.slice(j, i)); + if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i); + else pad = c === "e" ? " " : "0"; + if (format = formats[c]) c = format(date, pad); + string.push(c); + j = i + 1; + } + } + + string.push(specifier.slice(j, i)); + return string.join(""); + }; + } + + function newParse(specifier, newDate) { + return function(string) { + var d = newYear(1900), + i = parseSpecifier(d, specifier, string += "", 0), + week, day$$1; + if (i != string.length) return null; + + // If a UNIX timestamp is specified, return it. + if ("Q" in d) return new Date(d.Q); + + // The am-pm flag is 0 for AM, and 1 for PM. + if ("p" in d) d.H = d.H % 12 + d.p * 12; + + // Convert day-of-week and week-of-year to day-of-year. + if ("V" in d) { + if (d.V < 1 || d.V > 53) return null; + if (!("w" in d)) d.w = 1; + if ("Z" in d) { + week = utcDate(newYear(d.y)), day$$1 = week.getUTCDay(); + week = day$$1 > 4 || day$$1 === 0 ? utcMonday.ceil(week) : utcMonday(week); + week = utcDay.offset(week, (d.V - 1) * 7); + d.y = week.getUTCFullYear(); + d.m = week.getUTCMonth(); + d.d = week.getUTCDate() + (d.w + 6) % 7; + } else { + week = newDate(newYear(d.y)), day$$1 = week.getDay(); + week = day$$1 > 4 || day$$1 === 0 ? monday.ceil(week) : monday(week); + week = day.offset(week, (d.V - 1) * 7); + d.y = week.getFullYear(); + d.m = week.getMonth(); + d.d = week.getDate() + (d.w + 6) % 7; + } + } else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0; + day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay(); + d.m = 0; + d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$$1 + 5) % 7 : d.w + d.U * 7 - (day$$1 + 6) % 7; + } + + // If a time zone is specified, all fields are interpreted as UTC and then + // offset according to the specified time zone. + if ("Z" in d) { + d.H += d.Z / 100 | 0; + d.M += d.Z % 100; + return utcDate(d); + } + + // Otherwise, all fields are in local time. + return newDate(d); + }; + } + + function parseSpecifier(d, specifier, string, j) { + var i = 0, + n = specifier.length, + m = string.length, + c, + parse; + + while (i < n) { + if (j >= m) return -1; + c = specifier.charCodeAt(i++); + if (c === 37) { + c = specifier.charAt(i++); + parse = parses[c in pads ? specifier.charAt(i++) : c]; + if (!parse || ((j = parse(d, string, j)) < 0)) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + + return j; + } + + function parsePeriod(d, string, i) { + var n = periodRe.exec(string.slice(i)); + return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseShortWeekday(d, string, i) { + var n = shortWeekdayRe.exec(string.slice(i)); + return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseWeekday(d, string, i) { + var n = weekdayRe.exec(string.slice(i)); + return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseShortMonth(d, string, i) { + var n = shortMonthRe.exec(string.slice(i)); + return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseMonth(d, string, i) { + var n = monthRe.exec(string.slice(i)); + return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1; + } + + function parseLocaleDateTime(d, string, i) { + return parseSpecifier(d, locale_dateTime, string, i); + } + + function parseLocaleDate(d, string, i) { + return parseSpecifier(d, locale_date, string, i); + } + + function parseLocaleTime(d, string, i) { + return parseSpecifier(d, locale_time, string, i); + } + + function formatShortWeekday(d) { + return locale_shortWeekdays[d.getDay()]; + } + + function formatWeekday(d) { + return locale_weekdays[d.getDay()]; + } + + function formatShortMonth(d) { + return locale_shortMonths[d.getMonth()]; + } + + function formatMonth(d) { + return locale_months[d.getMonth()]; + } + + function formatPeriod(d) { + return locale_periods[+(d.getHours() >= 12)]; + } + + function formatUTCShortWeekday(d) { + return locale_shortWeekdays[d.getUTCDay()]; + } + + function formatUTCWeekday(d) { + return locale_weekdays[d.getUTCDay()]; + } + + function formatUTCShortMonth(d) { + return locale_shortMonths[d.getUTCMonth()]; + } + + function formatUTCMonth(d) { + return locale_months[d.getUTCMonth()]; + } + + function formatUTCPeriod(d) { + return locale_periods[+(d.getUTCHours() >= 12)]; + } + + return { + format: function(specifier) { + var f = newFormat(specifier += "", formats); + f.toString = function() { return specifier; }; + return f; + }, + parse: function(specifier) { + var p = newParse(specifier += "", localDate); + p.toString = function() { return specifier; }; + return p; + }, + utcFormat: function(specifier) { + var f = newFormat(specifier += "", utcFormats); + f.toString = function() { return specifier; }; + return f; + }, + utcParse: function(specifier) { + var p = newParse(specifier, utcDate); + p.toString = function() { return specifier; }; + return p; + } + }; +} + +var pads = {"-": "", "_": " ", "0": "0"}; +var numberRe = /^\s*\d+/; +var percentRe = /^%/; +var requoteRe = /[\\^$*+?|[\]().{}]/g; + +function pad(value, fill, width) { + var sign = value < 0 ? "-" : "", + string = (sign ? -value : value) + "", + length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); +} + +function requote(s) { + return s.replace(requoteRe, "\\$&"); +} + +function formatRe(names) { + return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i"); +} + +function formatLookup(names) { + var map = {}, i = -1, n = names.length; + while (++i < n) map[names[i].toLowerCase()] = i; + return map; +} + +function parseWeekdayNumberSunday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.w = +n[0], i + n[0].length) : -1; +} + +function parseWeekdayNumberMonday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 1)); + return n ? (d.u = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberSunday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.U = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberISO(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.V = +n[0], i + n[0].length) : -1; +} + +function parseWeekNumberMonday(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.W = +n[0], i + n[0].length) : -1; +} + +function parseFullYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 4)); + return n ? (d.y = +n[0], i + n[0].length) : -1; +} + +function parseYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1; +} + +function parseZone(d, string, i) { + var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6)); + return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1; +} + +function parseMonthNumber(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.m = n[0] - 1, i + n[0].length) : -1; +} + +function parseDayOfMonth(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.d = +n[0], i + n[0].length) : -1; +} + +function parseDayOfYear(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 3)); + return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1; +} + +function parseHour24(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.H = +n[0], i + n[0].length) : -1; +} + +function parseMinutes(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.M = +n[0], i + n[0].length) : -1; +} + +function parseSeconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 2)); + return n ? (d.S = +n[0], i + n[0].length) : -1; +} + +function parseMilliseconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 3)); + return n ? (d.L = +n[0], i + n[0].length) : -1; +} + +function parseMicroseconds(d, string, i) { + var n = numberRe.exec(string.slice(i, i + 6)); + return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1; +} + +function parseLiteralPercent(d, string, i) { + var n = percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; +} + +function parseUnixTimestamp(d, string, i) { + var n = numberRe.exec(string.slice(i)); + return n ? (d.Q = +n[0], i + n[0].length) : -1; +} + +function parseUnixTimestampSeconds(d, string, i) { + var n = numberRe.exec(string.slice(i)); + return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1; +} + +function formatDayOfMonth(d, p) { + return pad(d.getDate(), p, 2); +} + +function formatHour24(d, p) { + return pad(d.getHours(), p, 2); +} + +function formatHour12(d, p) { + return pad(d.getHours() % 12 || 12, p, 2); +} + +function formatDayOfYear(d, p) { + return pad(1 + day.count(year(d), d), p, 3); +} + +function formatMilliseconds(d, p) { + return pad(d.getMilliseconds(), p, 3); +} + +function formatMicroseconds(d, p) { + return formatMilliseconds(d, p) + "000"; +} + +function formatMonthNumber(d, p) { + return pad(d.getMonth() + 1, p, 2); +} + +function formatMinutes(d, p) { + return pad(d.getMinutes(), p, 2); +} + +function formatSeconds(d, p) { + return pad(d.getSeconds(), p, 2); +} + +function formatWeekdayNumberMonday(d) { + var day$$1 = d.getDay(); + return day$$1 === 0 ? 7 : day$$1; +} + +function formatWeekNumberSunday(d, p) { + return pad(sunday.count(year(d), d), p, 2); +} + +function formatWeekNumberISO(d, p) { + var day$$1 = d.getDay(); + d = (day$$1 >= 4 || day$$1 === 0) ? thursday(d) : thursday.ceil(d); + return pad(thursday.count(year(d), d) + (year(d).getDay() === 4), p, 2); +} + +function formatWeekdayNumberSunday(d) { + return d.getDay(); +} + +function formatWeekNumberMonday(d, p) { + return pad(monday.count(year(d), d), p, 2); +} + +function formatYear(d, p) { + return pad(d.getFullYear() % 100, p, 2); +} + +function formatFullYear(d, p) { + return pad(d.getFullYear() % 10000, p, 4); +} + +function formatZone(d) { + var z = d.getTimezoneOffset(); + return (z > 0 ? "-" : (z *= -1, "+")) + + pad(z / 60 | 0, "0", 2) + + pad(z % 60, "0", 2); +} + +function formatUTCDayOfMonth(d, p) { + return pad(d.getUTCDate(), p, 2); +} + +function formatUTCHour24(d, p) { + return pad(d.getUTCHours(), p, 2); +} + +function formatUTCHour12(d, p) { + return pad(d.getUTCHours() % 12 || 12, p, 2); +} + +function formatUTCDayOfYear(d, p) { + return pad(1 + utcDay.count(utcYear(d), d), p, 3); +} + +function formatUTCMilliseconds(d, p) { + return pad(d.getUTCMilliseconds(), p, 3); +} + +function formatUTCMicroseconds(d, p) { + return formatUTCMilliseconds(d, p) + "000"; +} + +function formatUTCMonthNumber(d, p) { + return pad(d.getUTCMonth() + 1, p, 2); +} + +function formatUTCMinutes(d, p) { + return pad(d.getUTCMinutes(), p, 2); +} + +function formatUTCSeconds(d, p) { + return pad(d.getUTCSeconds(), p, 2); +} + +function formatUTCWeekdayNumberMonday(d) { + var dow = d.getUTCDay(); + return dow === 0 ? 7 : dow; +} + +function formatUTCWeekNumberSunday(d, p) { + return pad(utcSunday.count(utcYear(d), d), p, 2); +} + +function formatUTCWeekNumberISO(d, p) { + var day$$1 = d.getUTCDay(); + d = (day$$1 >= 4 || day$$1 === 0) ? utcThursday(d) : utcThursday.ceil(d); + return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2); +} + +function formatUTCWeekdayNumberSunday(d) { + return d.getUTCDay(); +} + +function formatUTCWeekNumberMonday(d, p) { + return pad(utcMonday.count(utcYear(d), d), p, 2); +} + +function formatUTCYear(d, p) { + return pad(d.getUTCFullYear() % 100, p, 2); +} + +function formatUTCFullYear(d, p) { + return pad(d.getUTCFullYear() % 10000, p, 4); +} + +function formatUTCZone() { + return "+0000"; +} + +function formatLiteralPercent() { + return "%"; +} + +function formatUnixTimestamp(d) { + return +d; +} + +function formatUnixTimestampSeconds(d) { + return Math.floor(+d / 1000); +} + +var locale$1; + + + + + +defaultLocale$1({ + dateTime: "%x, %X", + date: "%-m/%-d/%Y", + time: "%-I:%M:%S %p", + periods: ["AM", "PM"], + days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] +}); + +function defaultLocale$1(definition) { + locale$1 = formatLocale$1(definition); + exports.timeFormat = locale$1.format; + exports.timeParse = locale$1.parse; + exports.utcFormat = locale$1.utcFormat; + exports.utcParse = locale$1.utcParse; + return locale$1; +} + +var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ"; + +function formatIsoNative(date) { + return date.toISOString(); +} + +var formatIso = Date.prototype.toISOString + ? formatIsoNative + : exports.utcFormat(isoSpecifier); + +function parseIsoNative(string) { + var date = new Date(string); + return isNaN(date) ? null : date; +} + +var parseIso = +new Date("2000-01-01T00:00:00.000Z") + ? parseIsoNative + : exports.utcParse(isoSpecifier); + +var durationSecond = 1000; +var durationMinute = durationSecond * 60; +var durationHour = durationMinute * 60; +var durationDay = durationHour * 24; +var durationWeek = durationDay * 7; +var durationMonth = durationDay * 30; +var durationYear = durationDay * 365; + +function date$1(t) { + return new Date(t); +} + +function number$3(t) { + return t instanceof Date ? +t : +new Date(+t); +} + +function calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format) { + var scale = continuous(deinterpolateLinear, reinterpolate), + invert = scale.invert, + domain = scale.domain; + + var formatMillisecond = format(".%L"), + formatSecond = format(":%S"), + formatMinute = format("%I:%M"), + formatHour = format("%I %p"), + formatDay = format("%a %d"), + formatWeek = format("%b %d"), + formatMonth = format("%B"), + formatYear = format("%Y"); + + var tickIntervals = [ + [second$$1, 1, durationSecond], + [second$$1, 5, 5 * durationSecond], + [second$$1, 15, 15 * durationSecond], + [second$$1, 30, 30 * durationSecond], + [minute$$1, 1, durationMinute], + [minute$$1, 5, 5 * durationMinute], + [minute$$1, 15, 15 * durationMinute], + [minute$$1, 30, 30 * durationMinute], + [ hour$$1, 1, durationHour ], + [ hour$$1, 3, 3 * durationHour ], + [ hour$$1, 6, 6 * durationHour ], + [ hour$$1, 12, 12 * durationHour ], + [ day$$1, 1, durationDay ], + [ day$$1, 2, 2 * durationDay ], + [ week, 1, durationWeek ], + [ month$$1, 1, durationMonth ], + [ month$$1, 3, 3 * durationMonth ], + [ year$$1, 1, durationYear ] + ]; + + function tickFormat(date) { + return (second$$1(date) < date ? formatMillisecond + : minute$$1(date) < date ? formatSecond + : hour$$1(date) < date ? formatMinute + : day$$1(date) < date ? formatHour + : month$$1(date) < date ? (week(date) < date ? formatDay : formatWeek) + : year$$1(date) < date ? formatMonth + : formatYear)(date); + } + + function tickInterval(interval, start, stop, step) { + if (interval == null) interval = 10; + + // If a desired tick count is specified, pick a reasonable tick interval + // based on the extent of the domain and a rough estimate of tick size. + // Otherwise, assume interval is already a time interval and use it. + if (typeof interval === "number") { + var target = Math.abs(stop - start) / interval, + i = bisector(function(i) { return i[2]; }).right(tickIntervals, target); + if (i === tickIntervals.length) { + step = tickStep(start / durationYear, stop / durationYear, interval); + interval = year$$1; + } else if (i) { + i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i]; + step = i[1]; + interval = i[0]; + } else { + step = Math.max(tickStep(start, stop, interval), 1); + interval = millisecond$$1; + } + } + + return step == null ? interval : interval.every(step); + } + + scale.invert = function(y) { + return new Date(invert(y)); + }; + + scale.domain = function(_) { + return arguments.length ? domain(map$3.call(_, number$3)) : domain().map(date$1); + }; + + scale.ticks = function(interval, step) { + var d = domain(), + t0 = d[0], + t1 = d[d.length - 1], + r = t1 < t0, + t; + if (r) t = t0, t0 = t1, t1 = t; + t = tickInterval(interval, t0, t1, step); + t = t ? t.range(t0, t1 + 1) : []; // inclusive stop + return r ? t.reverse() : t; + }; + + scale.tickFormat = function(count, specifier) { + return specifier == null ? tickFormat : format(specifier); + }; + + scale.nice = function(interval, step) { + var d = domain(); + return (interval = tickInterval(interval, d[0], d[d.length - 1], step)) + ? domain(nice(d, interval)) + : scale; + }; + + scale.copy = function() { + return copy(scale, calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format)); + }; + + return scale; +} + +function time() { + return calendar(year, month, sunday, day, hour, minute, second, millisecond, exports.timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]); +} + +function utcTime() { + return calendar(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, millisecond, exports.utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]); +} + +function colors(s) { + return s.match(/.{6}/g).map(function(x) { + return "#" + x; + }); +} + +var category10 = colors("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"); + +var category20b = colors("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6"); + +var category20c = colors("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9"); + +var category20 = colors("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5"); + +var cubehelix$3 = cubehelixLong(cubehelix(300, 0.5, 0.0), cubehelix(-240, 0.5, 1.0)); + +var warm = cubehelixLong(cubehelix(-100, 0.75, 0.35), cubehelix(80, 1.50, 0.8)); + +var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8)); + +var rainbow = cubehelix(); + +function rainbow$1(t) { + if (t < 0 || t > 1) t -= Math.floor(t); + var ts = Math.abs(t - 0.5); + rainbow.h = 360 * t - 100; + rainbow.s = 1.5 - 1.5 * ts; + rainbow.l = 0.8 - 0.9 * ts; + return rainbow + ""; +} + +function ramp(range) { + var n = range.length; + return function(t) { + return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))]; + }; +} + +var viridis = ramp(colors("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")); + +var magma = ramp(colors("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")); + +var inferno = ramp(colors("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")); + +var plasma = ramp(colors("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")); + +function sequential(interpolator) { + var x0 = 0, + x1 = 1, + clamp = false; + + function scale(x) { + var t = (x - x0) / (x1 - x0); + return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t); + } + + scale.domain = function(_) { + return arguments.length ? (x0 = +_[0], x1 = +_[1], scale) : [x0, x1]; + }; + + scale.clamp = function(_) { + return arguments.length ? (clamp = !!_, scale) : clamp; + }; + + scale.interpolator = function(_) { + return arguments.length ? (interpolator = _, scale) : interpolator; + }; + + scale.copy = function() { + return sequential(interpolator).domain([x0, x1]).clamp(clamp); + }; + + return linearish(scale); +} + +function constant$10(x) { + return function constant() { + return x; + }; +} + +var abs$1 = Math.abs; +var atan2$1 = Math.atan2; +var cos$2 = Math.cos; +var max$2 = Math.max; +var min$1 = Math.min; +var sin$2 = Math.sin; +var sqrt$2 = Math.sqrt; + +var epsilon$3 = 1e-12; +var pi$4 = Math.PI; +var halfPi$3 = pi$4 / 2; +var tau$4 = 2 * pi$4; + +function acos$1(x) { + return x > 1 ? 0 : x < -1 ? pi$4 : Math.acos(x); +} + +function asin$1(x) { + return x >= 1 ? halfPi$3 : x <= -1 ? -halfPi$3 : Math.asin(x); +} + +function arcInnerRadius(d) { + return d.innerRadius; +} + +function arcOuterRadius(d) { + return d.outerRadius; +} + +function arcStartAngle(d) { + return d.startAngle; +} + +function arcEndAngle(d) { + return d.endAngle; +} + +function arcPadAngle(d) { + return d && d.padAngle; // Note: optional! +} + +function intersect(x0, y0, x1, y1, x2, y2, x3, y3) { + var x10 = x1 - x0, y10 = y1 - y0, + x32 = x3 - x2, y32 = y3 - y2, + t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10); + return [x0 + t * x10, y0 + t * y10]; +} + +// Compute perpendicular offset line of length rc. +// http://mathworld.wolfram.com/Circle-LineIntersection.html +function cornerTangents(x0, y0, x1, y1, r1, rc, cw) { + var x01 = x0 - x1, + y01 = y0 - y1, + lo = (cw ? rc : -rc) / sqrt$2(x01 * x01 + y01 * y01), + ox = lo * y01, + oy = -lo * x01, + x11 = x0 + ox, + y11 = y0 + oy, + x10 = x1 + ox, + y10 = y1 + oy, + x00 = (x11 + x10) / 2, + y00 = (y11 + y10) / 2, + dx = x10 - x11, + dy = y10 - y11, + d2 = dx * dx + dy * dy, + r = r1 - rc, + D = x11 * y10 - x10 * y11, + d = (dy < 0 ? -1 : 1) * sqrt$2(max$2(0, r * r * d2 - D * D)), + cx0 = (D * dy - dx * d) / d2, + cy0 = (-D * dx - dy * d) / d2, + cx1 = (D * dy + dx * d) / d2, + cy1 = (-D * dx + dy * d) / d2, + dx0 = cx0 - x00, + dy0 = cy0 - y00, + dx1 = cx1 - x00, + dy1 = cy1 - y00; + + // Pick the closer of the two intersection points. + // TODO Is there a faster way to determine which intersection to use? + if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; + + return { + cx: cx0, + cy: cy0, + x01: -ox, + y01: -oy, + x11: cx0 * (r1 / r - 1), + y11: cy0 * (r1 / r - 1) + }; +} + +function arc() { + var innerRadius = arcInnerRadius, + outerRadius = arcOuterRadius, + cornerRadius = constant$10(0), + padRadius = null, + startAngle = arcStartAngle, + endAngle = arcEndAngle, + padAngle = arcPadAngle, + context = null; + + function arc() { + var buffer, + r, + r0 = +innerRadius.apply(this, arguments), + r1 = +outerRadius.apply(this, arguments), + a0 = startAngle.apply(this, arguments) - halfPi$3, + a1 = endAngle.apply(this, arguments) - halfPi$3, + da = abs$1(a1 - a0), + cw = a1 > a0; + + if (!context) context = buffer = path(); + + // Ensure that the outer radius is always larger than the inner radius. + if (r1 < r0) r = r1, r1 = r0, r0 = r; + + // Is it a point? + if (!(r1 > epsilon$3)) context.moveTo(0, 0); + + // Or is it a circle or annulus? + else if (da > tau$4 - epsilon$3) { + context.moveTo(r1 * cos$2(a0), r1 * sin$2(a0)); + context.arc(0, 0, r1, a0, a1, !cw); + if (r0 > epsilon$3) { + context.moveTo(r0 * cos$2(a1), r0 * sin$2(a1)); + context.arc(0, 0, r0, a1, a0, cw); + } + } + + // Or is it a circular or annular sector? + else { + var a01 = a0, + a11 = a1, + a00 = a0, + a10 = a1, + da0 = da, + da1 = da, + ap = padAngle.apply(this, arguments) / 2, + rp = (ap > epsilon$3) && (padRadius ? +padRadius.apply(this, arguments) : sqrt$2(r0 * r0 + r1 * r1)), + rc = min$1(abs$1(r1 - r0) / 2, +cornerRadius.apply(this, arguments)), + rc0 = rc, + rc1 = rc, + t0, + t1; + + // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0. + if (rp > epsilon$3) { + var p0 = asin$1(rp / r0 * sin$2(ap)), + p1 = asin$1(rp / r1 * sin$2(ap)); + if ((da0 -= p0 * 2) > epsilon$3) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0; + else da0 = 0, a00 = a10 = (a0 + a1) / 2; + if ((da1 -= p1 * 2) > epsilon$3) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1; + else da1 = 0, a01 = a11 = (a0 + a1) / 2; + } + + var x01 = r1 * cos$2(a01), + y01 = r1 * sin$2(a01), + x10 = r0 * cos$2(a10), + y10 = r0 * sin$2(a10); + + // Apply rounded corners? + if (rc > epsilon$3) { + var x11 = r1 * cos$2(a11), + y11 = r1 * sin$2(a11), + x00 = r0 * cos$2(a00), + y00 = r0 * sin$2(a00); + + // Restrict the corner radius according to the sector angle. + if (da < pi$4) { + var oc = da0 > epsilon$3 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10], + ax = x01 - oc[0], + ay = y01 - oc[1], + bx = x11 - oc[0], + by = y11 - oc[1], + kc = 1 / sin$2(acos$1((ax * bx + ay * by) / (sqrt$2(ax * ax + ay * ay) * sqrt$2(bx * bx + by * by))) / 2), + lc = sqrt$2(oc[0] * oc[0] + oc[1] * oc[1]); + rc0 = min$1(rc, (r0 - lc) / (kc - 1)); + rc1 = min$1(rc, (r1 - lc) / (kc + 1)); + } + } + + // Is the sector collapsed to a line? + if (!(da1 > epsilon$3)) context.moveTo(x01, y01); + + // Does the sector’s outer ring have rounded corners? + else if (rc1 > epsilon$3) { + t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw); + t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw); + + context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01); + + // Have the corners merged? + if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw); + + // Otherwise, draw the two corners and the ring. + else { + context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw); + context.arc(0, 0, r1, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), !cw); + context.arc(t1.cx, t1.cy, rc1, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw); + } + } + + // Or is the outer ring just a circular arc? + else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw); + + // Is there no inner ring, and it’s a circular sector? + // Or perhaps it’s an annular sector collapsed due to padding? + if (!(r0 > epsilon$3) || !(da0 > epsilon$3)) context.lineTo(x10, y10); + + // Does the sector’s inner ring (or point) have rounded corners? + else if (rc0 > epsilon$3) { + t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw); + t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw); + + context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01); + + // Have the corners merged? + if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw); + + // Otherwise, draw the two corners and the ring. + else { + context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw); + context.arc(0, 0, r0, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), cw); + context.arc(t1.cx, t1.cy, rc0, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw); + } + } + + // Or is the inner ring just a circular arc? + else context.arc(0, 0, r0, a10, a00, cw); + } + + context.closePath(); + + if (buffer) return context = null, buffer + "" || null; + } + + arc.centroid = function() { + var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, + a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$4 / 2; + return [cos$2(a) * r, sin$2(a) * r]; + }; + + arc.innerRadius = function(_) { + return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$10(+_), arc) : innerRadius; + }; + + arc.outerRadius = function(_) { + return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$10(+_), arc) : outerRadius; + }; + + arc.cornerRadius = function(_) { + return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$10(+_), arc) : cornerRadius; + }; + + arc.padRadius = function(_) { + return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$10(+_), arc) : padRadius; + }; + + arc.startAngle = function(_) { + return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$10(+_), arc) : startAngle; + }; + + arc.endAngle = function(_) { + return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$10(+_), arc) : endAngle; + }; + + arc.padAngle = function(_) { + return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$10(+_), arc) : padAngle; + }; + + arc.context = function(_) { + return arguments.length ? (context = _ == null ? null : _, arc) : context; + }; + + return arc; +} + +function Linear(context) { + this._context = context; +} + +Linear.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._point = 0; + }, + lineEnd: function() { + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; // proceed + default: this._context.lineTo(x, y); break; + } + } +}; + +function curveLinear(context) { + return new Linear(context); +} + +function x$3(p) { + return p[0]; +} + +function y$3(p) { + return p[1]; +} + +function line() { + var x$$1 = x$3, + y$$1 = y$3, + defined = constant$10(true), + context = null, + curve = curveLinear, + output = null; + + function line(data) { + var i, + n = data.length, + d, + defined0 = false, + buffer; + + if (context == null) output = curve(buffer = path()); + + for (i = 0; i <= n; ++i) { + if (!(i < n && defined(d = data[i], i, data)) === defined0) { + if (defined0 = !defined0) output.lineStart(); + else output.lineEnd(); + } + if (defined0) output.point(+x$$1(d, i, data), +y$$1(d, i, data)); + } + + if (buffer) return output = null, buffer + "" || null; + } + + line.x = function(_) { + return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$10(+_), line) : x$$1; + }; + + line.y = function(_) { + return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$10(+_), line) : y$$1; + }; + + line.defined = function(_) { + return arguments.length ? (defined = typeof _ === "function" ? _ : constant$10(!!_), line) : defined; + }; + + line.curve = function(_) { + return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve; + }; + + line.context = function(_) { + return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context; + }; + + return line; +} + +function area$2() { + var x0 = x$3, + x1 = null, + y0 = constant$10(0), + y1 = y$3, + defined = constant$10(true), + context = null, + curve = curveLinear, + output = null; + + function area(data) { + var i, + j, + k, + n = data.length, + d, + defined0 = false, + buffer, + x0z = new Array(n), + y0z = new Array(n); + + if (context == null) output = curve(buffer = path()); + + for (i = 0; i <= n; ++i) { + if (!(i < n && defined(d = data[i], i, data)) === defined0) { + if (defined0 = !defined0) { + j = i; + output.areaStart(); + output.lineStart(); + } else { + output.lineEnd(); + output.lineStart(); + for (k = i - 1; k >= j; --k) { + output.point(x0z[k], y0z[k]); + } + output.lineEnd(); + output.areaEnd(); + } + } + if (defined0) { + x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data); + output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]); + } + } + + if (buffer) return output = null, buffer + "" || null; + } + + function arealine() { + return line().defined(defined).curve(curve).context(context); + } + + area.x = function(_) { + return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$10(+_), x1 = null, area) : x0; + }; + + area.x0 = function(_) { + return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$10(+_), area) : x0; + }; + + area.x1 = function(_) { + return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$10(+_), area) : x1; + }; + + area.y = function(_) { + return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$10(+_), y1 = null, area) : y0; + }; + + area.y0 = function(_) { + return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$10(+_), area) : y0; + }; + + area.y1 = function(_) { + return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$10(+_), area) : y1; + }; + + area.lineX0 = + area.lineY0 = function() { + return arealine().x(x0).y(y0); + }; + + area.lineY1 = function() { + return arealine().x(x0).y(y1); + }; + + area.lineX1 = function() { + return arealine().x(x1).y(y0); + }; + + area.defined = function(_) { + return arguments.length ? (defined = typeof _ === "function" ? _ : constant$10(!!_), area) : defined; + }; + + area.curve = function(_) { + return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve; + }; + + area.context = function(_) { + return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context; + }; + + return area; +} + +function descending$1(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; +} + +function identity$7(d) { + return d; +} + +function pie() { + var value = identity$7, + sortValues = descending$1, + sort = null, + startAngle = constant$10(0), + endAngle = constant$10(tau$4), + padAngle = constant$10(0); + + function pie(data) { + var i, + n = data.length, + j, + k, + sum = 0, + index = new Array(n), + arcs = new Array(n), + a0 = +startAngle.apply(this, arguments), + da = Math.min(tau$4, Math.max(-tau$4, endAngle.apply(this, arguments) - a0)), + a1, + p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)), + pa = p * (da < 0 ? -1 : 1), + v; + + for (i = 0; i < n; ++i) { + if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) { + sum += v; + } + } + + // Optionally sort the arcs by previously-computed values or by data. + if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); }); + else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); }); + + // Compute the arcs! They are stored in the original data's order. + for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) { + j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = { + data: data[j], + index: i, + value: v, + startAngle: a0, + endAngle: a1, + padAngle: p + }; + } + + return arcs; + } + + pie.value = function(_) { + return arguments.length ? (value = typeof _ === "function" ? _ : constant$10(+_), pie) : value; + }; + + pie.sortValues = function(_) { + return arguments.length ? (sortValues = _, sort = null, pie) : sortValues; + }; + + pie.sort = function(_) { + return arguments.length ? (sort = _, sortValues = null, pie) : sort; + }; + + pie.startAngle = function(_) { + return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$10(+_), pie) : startAngle; + }; + + pie.endAngle = function(_) { + return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$10(+_), pie) : endAngle; + }; + + pie.padAngle = function(_) { + return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$10(+_), pie) : padAngle; + }; + + return pie; +} + +var curveRadialLinear = curveRadial(curveLinear); + +function Radial(curve) { + this._curve = curve; +} + +Radial.prototype = { + areaStart: function() { + this._curve.areaStart(); + }, + areaEnd: function() { + this._curve.areaEnd(); + }, + lineStart: function() { + this._curve.lineStart(); + }, + lineEnd: function() { + this._curve.lineEnd(); + }, + point: function(a, r) { + this._curve.point(r * Math.sin(a), r * -Math.cos(a)); + } +}; + +function curveRadial(curve) { + + function radial(context) { + return new Radial(curve(context)); + } + + radial._curve = curve; + + return radial; +} + +function lineRadial(l) { + var c = l.curve; + + l.angle = l.x, delete l.x; + l.radius = l.y, delete l.y; + + l.curve = function(_) { + return arguments.length ? c(curveRadial(_)) : c()._curve; + }; + + return l; +} + +function lineRadial$1() { + return lineRadial(line().curve(curveRadialLinear)); +} + +function areaRadial() { + var a = area$2().curve(curveRadialLinear), + c = a.curve, + x0 = a.lineX0, + x1 = a.lineX1, + y0 = a.lineY0, + y1 = a.lineY1; + + a.angle = a.x, delete a.x; + a.startAngle = a.x0, delete a.x0; + a.endAngle = a.x1, delete a.x1; + a.radius = a.y, delete a.y; + a.innerRadius = a.y0, delete a.y0; + a.outerRadius = a.y1, delete a.y1; + a.lineStartAngle = function() { return lineRadial(x0()); }, delete a.lineX0; + a.lineEndAngle = function() { return lineRadial(x1()); }, delete a.lineX1; + a.lineInnerRadius = function() { return lineRadial(y0()); }, delete a.lineY0; + a.lineOuterRadius = function() { return lineRadial(y1()); }, delete a.lineY1; + + a.curve = function(_) { + return arguments.length ? c(curveRadial(_)) : c()._curve; + }; + + return a; +} + +function pointRadial(x, y) { + return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)]; +} + +var slice$6 = Array.prototype.slice; + +function linkSource(d) { + return d.source; +} + +function linkTarget(d) { + return d.target; +} + +function link$2(curve) { + var source = linkSource, + target = linkTarget, + x$$1 = x$3, + y$$1 = y$3, + context = null; + + function link() { + var buffer, argv = slice$6.call(arguments), s = source.apply(this, argv), t = target.apply(this, argv); + if (!context) context = buffer = path(); + curve(context, +x$$1.apply(this, (argv[0] = s, argv)), +y$$1.apply(this, argv), +x$$1.apply(this, (argv[0] = t, argv)), +y$$1.apply(this, argv)); + if (buffer) return context = null, buffer + "" || null; + } + + link.source = function(_) { + return arguments.length ? (source = _, link) : source; + }; + + link.target = function(_) { + return arguments.length ? (target = _, link) : target; + }; + + link.x = function(_) { + return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$10(+_), link) : x$$1; + }; + + link.y = function(_) { + return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$10(+_), link) : y$$1; + }; + + link.context = function(_) { + return arguments.length ? (context = _ == null ? null : _, link) : context; + }; + + return link; +} + +function curveHorizontal(context, x0, y0, x1, y1) { + context.moveTo(x0, y0); + context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1); +} + +function curveVertical(context, x0, y0, x1, y1) { + context.moveTo(x0, y0); + context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1); +} + +function curveRadial$1(context, x0, y0, x1, y1) { + var p0 = pointRadial(x0, y0), + p1 = pointRadial(x0, y0 = (y0 + y1) / 2), + p2 = pointRadial(x1, y0), + p3 = pointRadial(x1, y1); + context.moveTo(p0[0], p0[1]); + context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]); +} + +function linkHorizontal() { + return link$2(curveHorizontal); +} + +function linkVertical() { + return link$2(curveVertical); +} + +function linkRadial() { + var l = link$2(curveRadial$1); + l.angle = l.x, delete l.x; + l.radius = l.y, delete l.y; + return l; +} + +var circle$2 = { + draw: function(context, size) { + var r = Math.sqrt(size / pi$4); + context.moveTo(r, 0); + context.arc(0, 0, r, 0, tau$4); + } +}; + +var cross$2 = { + draw: function(context, size) { + var r = Math.sqrt(size / 5) / 2; + context.moveTo(-3 * r, -r); + context.lineTo(-r, -r); + context.lineTo(-r, -3 * r); + context.lineTo(r, -3 * r); + context.lineTo(r, -r); + context.lineTo(3 * r, -r); + context.lineTo(3 * r, r); + context.lineTo(r, r); + context.lineTo(r, 3 * r); + context.lineTo(-r, 3 * r); + context.lineTo(-r, r); + context.lineTo(-3 * r, r); + context.closePath(); + } +}; + +var tan30 = Math.sqrt(1 / 3); +var tan30_2 = tan30 * 2; + +var diamond = { + draw: function(context, size) { + var y = Math.sqrt(size / tan30_2), + x = y * tan30; + context.moveTo(0, -y); + context.lineTo(x, 0); + context.lineTo(0, y); + context.lineTo(-x, 0); + context.closePath(); + } +}; + +var ka = 0.89081309152928522810; +var kr = Math.sin(pi$4 / 10) / Math.sin(7 * pi$4 / 10); +var kx = Math.sin(tau$4 / 10) * kr; +var ky = -Math.cos(tau$4 / 10) * kr; + +var star = { + draw: function(context, size) { + var r = Math.sqrt(size * ka), + x = kx * r, + y = ky * r; + context.moveTo(0, -r); + context.lineTo(x, y); + for (var i = 1; i < 5; ++i) { + var a = tau$4 * i / 5, + c = Math.cos(a), + s = Math.sin(a); + context.lineTo(s * r, -c * r); + context.lineTo(c * x - s * y, s * x + c * y); + } + context.closePath(); + } +}; + +var square = { + draw: function(context, size) { + var w = Math.sqrt(size), + x = -w / 2; + context.rect(x, x, w, w); + } +}; + +var sqrt3 = Math.sqrt(3); + +var triangle = { + draw: function(context, size) { + var y = -Math.sqrt(size / (sqrt3 * 3)); + context.moveTo(0, y * 2); + context.lineTo(-sqrt3 * y, -y); + context.lineTo(sqrt3 * y, -y); + context.closePath(); + } +}; + +var c = -0.5; +var s = Math.sqrt(3) / 2; +var k = 1 / Math.sqrt(12); +var a = (k / 2 + 1) * 3; + +var wye = { + draw: function(context, size) { + var r = Math.sqrt(size / a), + x0 = r / 2, + y0 = r * k, + x1 = x0, + y1 = r * k + r, + x2 = -x1, + y2 = y1; + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + context.lineTo(c * x0 - s * y0, s * x0 + c * y0); + context.lineTo(c * x1 - s * y1, s * x1 + c * y1); + context.lineTo(c * x2 - s * y2, s * x2 + c * y2); + context.lineTo(c * x0 + s * y0, c * y0 - s * x0); + context.lineTo(c * x1 + s * y1, c * y1 - s * x1); + context.lineTo(c * x2 + s * y2, c * y2 - s * x2); + context.closePath(); + } +}; + +var symbols = [ + circle$2, + cross$2, + diamond, + square, + star, + triangle, + wye +]; + +function symbol() { + var type = constant$10(circle$2), + size = constant$10(64), + context = null; + + function symbol() { + var buffer; + if (!context) context = buffer = path(); + type.apply(this, arguments).draw(context, +size.apply(this, arguments)); + if (buffer) return context = null, buffer + "" || null; + } + + symbol.type = function(_) { + return arguments.length ? (type = typeof _ === "function" ? _ : constant$10(_), symbol) : type; + }; + + symbol.size = function(_) { + return arguments.length ? (size = typeof _ === "function" ? _ : constant$10(+_), symbol) : size; + }; + + symbol.context = function(_) { + return arguments.length ? (context = _ == null ? null : _, symbol) : context; + }; + + return symbol; +} + +function noop$2() {} + +function point$2(that, x, y) { + that._context.bezierCurveTo( + (2 * that._x0 + that._x1) / 3, + (2 * that._y0 + that._y1) / 3, + (that._x0 + 2 * that._x1) / 3, + (that._y0 + 2 * that._y1) / 3, + (that._x0 + 4 * that._x1 + x) / 6, + (that._y0 + 4 * that._y1 + y) / 6 + ); +} + +function Basis(context) { + this._context = context; +} + +Basis.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = + this._y0 = this._y1 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 3: point$2(this, this._x1, this._y1); // proceed + case 2: this._context.lineTo(this._x1, this._y1); break; + } + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; break; + case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed + default: point$2(this, x, y); break; + } + this._x0 = this._x1, this._x1 = x; + this._y0 = this._y1, this._y1 = y; + } +}; + +function basis$2(context) { + return new Basis(context); +} + +function BasisClosed(context) { + this._context = context; +} + +BasisClosed.prototype = { + areaStart: noop$2, + areaEnd: noop$2, + lineStart: function() { + this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = + this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 1: { + this._context.moveTo(this._x2, this._y2); + this._context.closePath(); + break; + } + case 2: { + this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3); + this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3); + this._context.closePath(); + break; + } + case 3: { + this.point(this._x2, this._y2); + this.point(this._x3, this._y3); + this.point(this._x4, this._y4); + break; + } + } + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._x2 = x, this._y2 = y; break; + case 1: this._point = 2; this._x3 = x, this._y3 = y; break; + case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break; + default: point$2(this, x, y); break; + } + this._x0 = this._x1, this._x1 = x; + this._y0 = this._y1, this._y1 = y; + } +}; + +function basisClosed$1(context) { + return new BasisClosed(context); +} + +function BasisOpen(context) { + this._context = context; +} + +BasisOpen.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = + this._y0 = this._y1 = NaN; + this._point = 0; + }, + lineEnd: function() { + if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; break; + case 1: this._point = 2; break; + case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break; + case 3: this._point = 4; // proceed + default: point$2(this, x, y); break; + } + this._x0 = this._x1, this._x1 = x; + this._y0 = this._y1, this._y1 = y; + } +}; + +function basisOpen(context) { + return new BasisOpen(context); +} + +function Bundle(context, beta) { + this._basis = new Basis(context); + this._beta = beta; +} + +Bundle.prototype = { + lineStart: function() { + this._x = []; + this._y = []; + this._basis.lineStart(); + }, + lineEnd: function() { + var x = this._x, + y = this._y, + j = x.length - 1; + + if (j > 0) { + var x0 = x[0], + y0 = y[0], + dx = x[j] - x0, + dy = y[j] - y0, + i = -1, + t; + + while (++i <= j) { + t = i / j; + this._basis.point( + this._beta * x[i] + (1 - this._beta) * (x0 + t * dx), + this._beta * y[i] + (1 - this._beta) * (y0 + t * dy) + ); + } + } + + this._x = this._y = null; + this._basis.lineEnd(); + }, + point: function(x, y) { + this._x.push(+x); + this._y.push(+y); + } +}; + +var bundle = (function custom(beta) { + + function bundle(context) { + return beta === 1 ? new Basis(context) : new Bundle(context, beta); + } + + bundle.beta = function(beta) { + return custom(+beta); + }; + + return bundle; +})(0.85); + +function point$3(that, x, y) { + that._context.bezierCurveTo( + that._x1 + that._k * (that._x2 - that._x0), + that._y1 + that._k * (that._y2 - that._y0), + that._x2 + that._k * (that._x1 - x), + that._y2 + that._k * (that._y1 - y), + that._x2, + that._y2 + ); +} + +function Cardinal(context, tension) { + this._context = context; + this._k = (1 - tension) / 6; +} + +Cardinal.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = this._x2 = + this._y0 = this._y1 = this._y2 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 2: this._context.lineTo(this._x2, this._y2); break; + case 3: point$3(this, this._x1, this._y1); break; + } + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; this._x1 = x, this._y1 = y; break; + case 2: this._point = 3; // proceed + default: point$3(this, x, y); break; + } + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var cardinal = (function custom(tension) { + + function cardinal(context) { + return new Cardinal(context, tension); + } + + cardinal.tension = function(tension) { + return custom(+tension); + }; + + return cardinal; +})(0); + +function CardinalClosed(context, tension) { + this._context = context; + this._k = (1 - tension) / 6; +} + +CardinalClosed.prototype = { + areaStart: noop$2, + areaEnd: noop$2, + lineStart: function() { + this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = + this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 1: { + this._context.moveTo(this._x3, this._y3); + this._context.closePath(); + break; + } + case 2: { + this._context.lineTo(this._x3, this._y3); + this._context.closePath(); + break; + } + case 3: { + this.point(this._x3, this._y3); + this.point(this._x4, this._y4); + this.point(this._x5, this._y5); + break; + } + } + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._x3 = x, this._y3 = y; break; + case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break; + case 2: this._point = 3; this._x5 = x, this._y5 = y; break; + default: point$3(this, x, y); break; + } + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var cardinalClosed = (function custom(tension) { + + function cardinal$$1(context) { + return new CardinalClosed(context, tension); + } + + cardinal$$1.tension = function(tension) { + return custom(+tension); + }; + + return cardinal$$1; +})(0); + +function CardinalOpen(context, tension) { + this._context = context; + this._k = (1 - tension) / 6; +} + +CardinalOpen.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = this._x2 = + this._y0 = this._y1 = this._y2 = NaN; + this._point = 0; + }, + lineEnd: function() { + if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; break; + case 1: this._point = 2; break; + case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break; + case 3: this._point = 4; // proceed + default: point$3(this, x, y); break; + } + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var cardinalOpen = (function custom(tension) { + + function cardinal$$1(context) { + return new CardinalOpen(context, tension); + } + + cardinal$$1.tension = function(tension) { + return custom(+tension); + }; + + return cardinal$$1; +})(0); + +function point$4(that, x, y) { + var x1 = that._x1, + y1 = that._y1, + x2 = that._x2, + y2 = that._y2; + + if (that._l01_a > epsilon$3) { + var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a, + n = 3 * that._l01_a * (that._l01_a + that._l12_a); + x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n; + y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n; + } + + if (that._l23_a > epsilon$3) { + var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a, + m = 3 * that._l23_a * (that._l23_a + that._l12_a); + x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m; + y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m; + } + + that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2); +} + +function CatmullRom(context, alpha) { + this._context = context; + this._alpha = alpha; +} + +CatmullRom.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = this._x2 = + this._y0 = this._y1 = this._y2 = NaN; + this._l01_a = this._l12_a = this._l23_a = + this._l01_2a = this._l12_2a = this._l23_2a = + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 2: this._context.lineTo(this._x2, this._y2); break; + case 3: this.point(this._x2, this._y2); break; + } + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + + if (this._point) { + var x23 = this._x2 - x, + y23 = this._y2 - y; + this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); + } + + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; break; + case 2: this._point = 3; // proceed + default: point$4(this, x, y); break; + } + + this._l01_a = this._l12_a, this._l12_a = this._l23_a; + this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var catmullRom = (function custom(alpha) { + + function catmullRom(context) { + return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0); + } + + catmullRom.alpha = function(alpha) { + return custom(+alpha); + }; + + return catmullRom; +})(0.5); + +function CatmullRomClosed(context, alpha) { + this._context = context; + this._alpha = alpha; +} + +CatmullRomClosed.prototype = { + areaStart: noop$2, + areaEnd: noop$2, + lineStart: function() { + this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = + this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN; + this._l01_a = this._l12_a = this._l23_a = + this._l01_2a = this._l12_2a = this._l23_2a = + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 1: { + this._context.moveTo(this._x3, this._y3); + this._context.closePath(); + break; + } + case 2: { + this._context.lineTo(this._x3, this._y3); + this._context.closePath(); + break; + } + case 3: { + this.point(this._x3, this._y3); + this.point(this._x4, this._y4); + this.point(this._x5, this._y5); + break; + } + } + }, + point: function(x, y) { + x = +x, y = +y; + + if (this._point) { + var x23 = this._x2 - x, + y23 = this._y2 - y; + this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); + } + + switch (this._point) { + case 0: this._point = 1; this._x3 = x, this._y3 = y; break; + case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break; + case 2: this._point = 3; this._x5 = x, this._y5 = y; break; + default: point$4(this, x, y); break; + } + + this._l01_a = this._l12_a, this._l12_a = this._l23_a; + this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var catmullRomClosed = (function custom(alpha) { + + function catmullRom$$1(context) { + return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0); + } + + catmullRom$$1.alpha = function(alpha) { + return custom(+alpha); + }; + + return catmullRom$$1; +})(0.5); + +function CatmullRomOpen(context, alpha) { + this._context = context; + this._alpha = alpha; +} + +CatmullRomOpen.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = this._x2 = + this._y0 = this._y1 = this._y2 = NaN; + this._l01_a = this._l12_a = this._l23_a = + this._l01_2a = this._l12_2a = this._l23_2a = + this._point = 0; + }, + lineEnd: function() { + if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + + if (this._point) { + var x23 = this._x2 - x, + y23 = this._y2 - y; + this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); + } + + switch (this._point) { + case 0: this._point = 1; break; + case 1: this._point = 2; break; + case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break; + case 3: this._point = 4; // proceed + default: point$4(this, x, y); break; + } + + this._l01_a = this._l12_a, this._l12_a = this._l23_a; + this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; + this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; + this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; + } +}; + +var catmullRomOpen = (function custom(alpha) { + + function catmullRom$$1(context) { + return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0); + } + + catmullRom$$1.alpha = function(alpha) { + return custom(+alpha); + }; + + return catmullRom$$1; +})(0.5); + +function LinearClosed(context) { + this._context = context; +} + +LinearClosed.prototype = { + areaStart: noop$2, + areaEnd: noop$2, + lineStart: function() { + this._point = 0; + }, + lineEnd: function() { + if (this._point) this._context.closePath(); + }, + point: function(x, y) { + x = +x, y = +y; + if (this._point) this._context.lineTo(x, y); + else this._point = 1, this._context.moveTo(x, y); + } +}; + +function linearClosed(context) { + return new LinearClosed(context); +} + +function sign$1(x) { + return x < 0 ? -1 : 1; +} + +// Calculate the slopes of the tangents (Hermite-type interpolation) based on +// the following paper: Steffen, M. 1990. A Simple Method for Monotonic +// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO. +// NOV(II), P. 443, 1990. +function slope3(that, x2, y2) { + var h0 = that._x1 - that._x0, + h1 = x2 - that._x1, + s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0), + s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0), + p = (s0 * h1 + s1 * h0) / (h0 + h1); + return (sign$1(s0) + sign$1(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0; +} + +// Calculate a one-sided slope. +function slope2(that, t) { + var h = that._x1 - that._x0; + return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t; +} + +// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations +// "you can express cubic Hermite interpolation in terms of cubic Bézier curves +// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1". +function point$5(that, t0, t1) { + var x0 = that._x0, + y0 = that._y0, + x1 = that._x1, + y1 = that._y1, + dx = (x1 - x0) / 3; + that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1); +} + +function MonotoneX(context) { + this._context = context; +} + +MonotoneX.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x0 = this._x1 = + this._y0 = this._y1 = + this._t0 = NaN; + this._point = 0; + }, + lineEnd: function() { + switch (this._point) { + case 2: this._context.lineTo(this._x1, this._y1); break; + case 3: point$5(this, this._t0, slope2(this, this._t0)); break; + } + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + this._line = 1 - this._line; + }, + point: function(x, y) { + var t1 = NaN; + + x = +x, y = +y; + if (x === this._x1 && y === this._y1) return; // Ignore coincident points. + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; break; + case 2: this._point = 3; point$5(this, slope2(this, t1 = slope3(this, x, y)), t1); break; + default: point$5(this, this._t0, t1 = slope3(this, x, y)); break; + } + + this._x0 = this._x1, this._x1 = x; + this._y0 = this._y1, this._y1 = y; + this._t0 = t1; + } +}; + +function MonotoneY(context) { + this._context = new ReflectContext(context); +} + +(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) { + MonotoneX.prototype.point.call(this, y, x); +}; + +function ReflectContext(context) { + this._context = context; +} + +ReflectContext.prototype = { + moveTo: function(x, y) { this._context.moveTo(y, x); }, + closePath: function() { this._context.closePath(); }, + lineTo: function(x, y) { this._context.lineTo(y, x); }, + bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); } +}; + +function monotoneX(context) { + return new MonotoneX(context); +} + +function monotoneY(context) { + return new MonotoneY(context); +} + +function Natural(context) { + this._context = context; +} + +Natural.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x = []; + this._y = []; + }, + lineEnd: function() { + var x = this._x, + y = this._y, + n = x.length; + + if (n) { + this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]); + if (n === 2) { + this._context.lineTo(x[1], y[1]); + } else { + var px = controlPoints(x), + py = controlPoints(y); + for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) { + this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]); + } + } + } + + if (this._line || (this._line !== 0 && n === 1)) this._context.closePath(); + this._line = 1 - this._line; + this._x = this._y = null; + }, + point: function(x, y) { + this._x.push(+x); + this._y.push(+y); + } +}; + +// See https://www.particleincell.com/2012/bezier-splines/ for derivation. +function controlPoints(x) { + var i, + n = x.length - 1, + m, + a = new Array(n), + b = new Array(n), + r = new Array(n); + a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1]; + for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1]; + a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n]; + for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1]; + a[n - 1] = r[n - 1] / b[n - 1]; + for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i]; + b[n - 1] = (x[n] + a[n - 1]) / 2; + for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1]; + return [a, b]; +} + +function natural(context) { + return new Natural(context); +} + +function Step(context, t) { + this._context = context; + this._t = t; +} + +Step.prototype = { + areaStart: function() { + this._line = 0; + }, + areaEnd: function() { + this._line = NaN; + }, + lineStart: function() { + this._x = this._y = NaN; + this._point = 0; + }, + lineEnd: function() { + if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y); + if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); + if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line; + }, + point: function(x, y) { + x = +x, y = +y; + switch (this._point) { + case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; + case 1: this._point = 2; // proceed + default: { + if (this._t <= 0) { + this._context.lineTo(this._x, y); + this._context.lineTo(x, y); + } else { + var x1 = this._x * (1 - this._t) + x * this._t; + this._context.lineTo(x1, this._y); + this._context.lineTo(x1, y); + } + break; + } + } + this._x = x, this._y = y; + } +}; + +function step(context) { + return new Step(context, 0.5); +} + +function stepBefore(context) { + return new Step(context, 0); +} + +function stepAfter(context) { + return new Step(context, 1); +} + +function none$1(series, order) { + if (!((n = series.length) > 1)) return; + for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) { + s0 = s1, s1 = series[order[i]]; + for (j = 0; j < m; ++j) { + s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1]; + } + } +} + +function none$2(series) { + var n = series.length, o = new Array(n); + while (--n >= 0) o[n] = n; + return o; +} + +function stackValue(d, key) { + return d[key]; +} + +function stack() { + var keys = constant$10([]), + order = none$2, + offset = none$1, + value = stackValue; + + function stack(data) { + var kz = keys.apply(this, arguments), + i, + m = data.length, + n = kz.length, + sz = new Array(n), + oz; + + for (i = 0; i < n; ++i) { + for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) { + si[j] = sij = [0, +value(data[j], ki, j, data)]; + sij.data = data[j]; + } + si.key = ki; + } + + for (i = 0, oz = order(sz); i < n; ++i) { + sz[oz[i]].index = i; + } + + offset(sz, oz); + return sz; + } + + stack.keys = function(_) { + return arguments.length ? (keys = typeof _ === "function" ? _ : constant$10(slice$6.call(_)), stack) : keys; + }; + + stack.value = function(_) { + return arguments.length ? (value = typeof _ === "function" ? _ : constant$10(+_), stack) : value; + }; + + stack.order = function(_) { + return arguments.length ? (order = _ == null ? none$2 : typeof _ === "function" ? _ : constant$10(slice$6.call(_)), stack) : order; + }; + + stack.offset = function(_) { + return arguments.length ? (offset = _ == null ? none$1 : _, stack) : offset; + }; + + return stack; +} + +function expand(series, order) { + if (!((n = series.length) > 0)) return; + for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) { + for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0; + if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y; + } + none$1(series, order); +} + +function diverging(series, order) { + if (!((n = series.length) > 1)) return; + for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) { + for (yp = yn = 0, i = 0; i < n; ++i) { + if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) { + d[0] = yp, d[1] = yp += dy; + } else if (dy < 0) { + d[1] = yn, d[0] = yn += dy; + } else { + d[0] = yp; + } + } + } +} + +function silhouette(series, order) { + if (!((n = series.length) > 0)) return; + for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) { + for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0; + s0[j][1] += s0[j][0] = -y / 2; + } + none$1(series, order); +} + +function wiggle(series, order) { + if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return; + for (var y = 0, j = 1, s0, m, n; j < m; ++j) { + for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) { + var si = series[order[i]], + sij0 = si[j][1] || 0, + sij1 = si[j - 1][1] || 0, + s3 = (sij0 - sij1) / 2; + for (var k = 0; k < i; ++k) { + var sk = series[order[k]], + skj0 = sk[j][1] || 0, + skj1 = sk[j - 1][1] || 0; + s3 += skj0 - skj1; + } + s1 += sij0, s2 += s3 * sij0; + } + s0[j - 1][1] += s0[j - 1][0] = y; + if (s1) y -= s2 / s1; + } + s0[j - 1][1] += s0[j - 1][0] = y; + none$1(series, order); +} + +function ascending$2(series) { + var sums = series.map(sum$2); + return none$2(series).sort(function(a, b) { return sums[a] - sums[b]; }); +} + +function sum$2(series) { + var s = 0, i = -1, n = series.length, v; + while (++i < n) if (v = +series[i][1]) s += v; + return s; +} + +function descending$2(series) { + return ascending$2(series).reverse(); +} + +function insideOut(series) { + var n = series.length, + i, + j, + sums = series.map(sum$2), + order = none$2(series).sort(function(a, b) { return sums[b] - sums[a]; }), + top = 0, + bottom = 0, + tops = [], + bottoms = []; + + for (i = 0; i < n; ++i) { + j = order[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + + return bottoms.reverse().concat(tops); +} + +function reverse(series) { + return none$2(series).reverse(); +} + +function constant$11(x) { + return function() { + return x; + }; +} + +function x$4(d) { + return d[0]; +} + +function y$4(d) { + return d[1]; +} + +function RedBlackTree() { + this._ = null; // root node +} + +function RedBlackNode(node) { + node.U = // parent node + node.C = // color - true for red, false for black + node.L = // left node + node.R = // right node + node.P = // previous node + node.N = null; // next node +} + +RedBlackTree.prototype = { + constructor: RedBlackTree, + + insert: function(after, node) { + var parent, grandpa, uncle; + + if (after) { + node.P = after; + node.N = after.N; + if (after.N) after.N.P = node; + after.N = node; + if (after.R) { + after = after.R; + while (after.L) after = after.L; + after.L = node; + } else { + after.R = node; + } + parent = after; + } else if (this._) { + after = RedBlackFirst(this._); + node.P = null; + node.N = after; + after.P = after.L = node; + parent = after; + } else { + node.P = node.N = null; + this._ = node; + parent = null; + } + node.L = node.R = null; + node.U = parent; + node.C = true; + + after = node; + while (parent && parent.C) { + grandpa = parent.U; + if (parent === grandpa.L) { + uncle = grandpa.R; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.R) { + RedBlackRotateLeft(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + RedBlackRotateRight(this, grandpa); + } + } else { + uncle = grandpa.L; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.L) { + RedBlackRotateRight(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + RedBlackRotateLeft(this, grandpa); + } + } + parent = after.U; + } + this._.C = false; + }, + + remove: function(node) { + if (node.N) node.N.P = node.P; + if (node.P) node.P.N = node.N; + node.N = node.P = null; + + var parent = node.U, + sibling, + left = node.L, + right = node.R, + next, + red; + + if (!left) next = right; + else if (!right) next = left; + else next = RedBlackFirst(right); + + if (parent) { + if (parent.L === node) parent.L = next; + else parent.R = next; + } else { + this._ = next; + } + + if (left && right) { + red = next.C; + next.C = node.C; + next.L = left; + left.U = next; + if (next !== right) { + parent = next.U; + next.U = node.U; + node = next.R; + parent.L = node; + next.R = right; + right.U = next; + } else { + next.U = parent; + parent = next; + node = next.R; + } + } else { + red = node.C; + node = next; + } + + if (node) node.U = parent; + if (red) return; + if (node && node.C) { node.C = false; return; } + + do { + if (node === this._) break; + if (node === parent.L) { + sibling = parent.R; + if (sibling.C) { + sibling.C = false; + parent.C = true; + RedBlackRotateLeft(this, parent); + sibling = parent.R; + } + if ((sibling.L && sibling.L.C) + || (sibling.R && sibling.R.C)) { + if (!sibling.R || !sibling.R.C) { + sibling.L.C = false; + sibling.C = true; + RedBlackRotateRight(this, sibling); + sibling = parent.R; + } + sibling.C = parent.C; + parent.C = sibling.R.C = false; + RedBlackRotateLeft(this, parent); + node = this._; + break; + } + } else { + sibling = parent.L; + if (sibling.C) { + sibling.C = false; + parent.C = true; + RedBlackRotateRight(this, parent); + sibling = parent.L; + } + if ((sibling.L && sibling.L.C) + || (sibling.R && sibling.R.C)) { + if (!sibling.L || !sibling.L.C) { + sibling.R.C = false; + sibling.C = true; + RedBlackRotateLeft(this, sibling); + sibling = parent.L; + } + sibling.C = parent.C; + parent.C = sibling.L.C = false; + RedBlackRotateRight(this, parent); + node = this._; + break; + } + } + sibling.C = true; + node = parent; + parent = parent.U; + } while (!node.C); + + if (node) node.C = false; + } +}; + +function RedBlackRotateLeft(tree, node) { + var p = node, + q = node.R, + parent = p.U; + + if (parent) { + if (parent.L === p) parent.L = q; + else parent.R = q; + } else { + tree._ = q; + } + + q.U = parent; + p.U = q; + p.R = q.L; + if (p.R) p.R.U = p; + q.L = p; +} + +function RedBlackRotateRight(tree, node) { + var p = node, + q = node.L, + parent = p.U; + + if (parent) { + if (parent.L === p) parent.L = q; + else parent.R = q; + } else { + tree._ = q; + } + + q.U = parent; + p.U = q; + p.L = q.R; + if (p.L) p.L.U = p; + q.R = p; +} + +function RedBlackFirst(node) { + while (node.L) node = node.L; + return node; +} + +function createEdge(left, right, v0, v1) { + var edge = [null, null], + index = edges.push(edge) - 1; + edge.left = left; + edge.right = right; + if (v0) setEdgeEnd(edge, left, right, v0); + if (v1) setEdgeEnd(edge, right, left, v1); + cells[left.index].halfedges.push(index); + cells[right.index].halfedges.push(index); + return edge; +} + +function createBorderEdge(left, v0, v1) { + var edge = [v0, v1]; + edge.left = left; + return edge; +} + +function setEdgeEnd(edge, left, right, vertex) { + if (!edge[0] && !edge[1]) { + edge[0] = vertex; + edge.left = left; + edge.right = right; + } else if (edge.left === right) { + edge[1] = vertex; + } else { + edge[0] = vertex; + } +} + +// Liang–Barsky line clipping. +function clipEdge(edge, x0, y0, x1, y1) { + var a = edge[0], + b = edge[1], + ax = a[0], + ay = a[1], + bx = b[0], + by = b[1], + t0 = 0, + t1 = 1, + dx = bx - ax, + dy = by - ay, + r; + + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + + if (!(t0 > 0) && !(t1 < 1)) return true; // TODO Better check? + + if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy]; + if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy]; + return true; +} + +function connectEdge(edge, x0, y0, x1, y1) { + var v1 = edge[1]; + if (v1) return true; + + var v0 = edge[0], + left = edge.left, + right = edge.right, + lx = left[0], + ly = left[1], + rx = right[0], + ry = right[1], + fx = (lx + rx) / 2, + fy = (ly + ry) / 2, + fm, + fb; + + if (ry === ly) { + if (fx < x0 || fx >= x1) return; + if (lx > rx) { + if (!v0) v0 = [fx, y0]; + else if (v0[1] >= y1) return; + v1 = [fx, y1]; + } else { + if (!v0) v0 = [fx, y1]; + else if (v0[1] < y0) return; + v1 = [fx, y0]; + } + } else { + fm = (lx - rx) / (ry - ly); + fb = fy - fm * fx; + if (fm < -1 || fm > 1) { + if (lx > rx) { + if (!v0) v0 = [(y0 - fb) / fm, y0]; + else if (v0[1] >= y1) return; + v1 = [(y1 - fb) / fm, y1]; + } else { + if (!v0) v0 = [(y1 - fb) / fm, y1]; + else if (v0[1] < y0) return; + v1 = [(y0 - fb) / fm, y0]; + } + } else { + if (ly < ry) { + if (!v0) v0 = [x0, fm * x0 + fb]; + else if (v0[0] >= x1) return; + v1 = [x1, fm * x1 + fb]; + } else { + if (!v0) v0 = [x1, fm * x1 + fb]; + else if (v0[0] < x0) return; + v1 = [x0, fm * x0 + fb]; + } + } + } + + edge[0] = v0; + edge[1] = v1; + return true; +} + +function clipEdges(x0, y0, x1, y1) { + var i = edges.length, + edge; + + while (i--) { + if (!connectEdge(edge = edges[i], x0, y0, x1, y1) + || !clipEdge(edge, x0, y0, x1, y1) + || !(Math.abs(edge[0][0] - edge[1][0]) > epsilon$4 + || Math.abs(edge[0][1] - edge[1][1]) > epsilon$4)) { + delete edges[i]; + } + } +} + +function createCell(site) { + return cells[site.index] = { + site: site, + halfedges: [] + }; +} + +function cellHalfedgeAngle(cell, edge) { + var site = cell.site, + va = edge.left, + vb = edge.right; + if (site === vb) vb = va, va = site; + if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]); + if (site === va) va = edge[1], vb = edge[0]; + else va = edge[0], vb = edge[1]; + return Math.atan2(va[0] - vb[0], vb[1] - va[1]); +} + +function cellHalfedgeStart(cell, edge) { + return edge[+(edge.left !== cell.site)]; +} + +function cellHalfedgeEnd(cell, edge) { + return edge[+(edge.left === cell.site)]; +} + +function sortCellHalfedges() { + for (var i = 0, n = cells.length, cell, halfedges, j, m; i < n; ++i) { + if ((cell = cells[i]) && (m = (halfedges = cell.halfedges).length)) { + var index = new Array(m), + array = new Array(m); + for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, edges[halfedges[j]]); + index.sort(function(i, j) { return array[j] - array[i]; }); + for (j = 0; j < m; ++j) array[j] = halfedges[index[j]]; + for (j = 0; j < m; ++j) halfedges[j] = array[j]; + } + } +} + +function clipCells(x0, y0, x1, y1) { + var nCells = cells.length, + iCell, + cell, + site, + iHalfedge, + halfedges, + nHalfedges, + start, + startX, + startY, + end, + endX, + endY, + cover = true; + + for (iCell = 0; iCell < nCells; ++iCell) { + if (cell = cells[iCell]) { + site = cell.site; + halfedges = cell.halfedges; + iHalfedge = halfedges.length; + + // Remove any dangling clipped edges. + while (iHalfedge--) { + if (!edges[halfedges[iHalfedge]]) { + halfedges.splice(iHalfedge, 1); + } + } + + // Insert any border edges as necessary. + iHalfedge = 0, nHalfedges = halfedges.length; + while (iHalfedge < nHalfedges) { + end = cellHalfedgeEnd(cell, edges[halfedges[iHalfedge]]), endX = end[0], endY = end[1]; + start = cellHalfedgeStart(cell, edges[halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1]; + if (Math.abs(endX - startX) > epsilon$4 || Math.abs(endY - startY) > epsilon$4) { + halfedges.splice(iHalfedge, 0, edges.push(createBorderEdge(site, end, + Math.abs(endX - x0) < epsilon$4 && y1 - endY > epsilon$4 ? [x0, Math.abs(startX - x0) < epsilon$4 ? startY : y1] + : Math.abs(endY - y1) < epsilon$4 && x1 - endX > epsilon$4 ? [Math.abs(startY - y1) < epsilon$4 ? startX : x1, y1] + : Math.abs(endX - x1) < epsilon$4 && endY - y0 > epsilon$4 ? [x1, Math.abs(startX - x1) < epsilon$4 ? startY : y0] + : Math.abs(endY - y0) < epsilon$4 && endX - x0 > epsilon$4 ? [Math.abs(startY - y0) < epsilon$4 ? startX : x0, y0] + : null)) - 1); + ++nHalfedges; + } + } + + if (nHalfedges) cover = false; + } + } + + // If there weren’t any edges, have the closest site cover the extent. + // It doesn’t matter which corner of the extent we measure! + if (cover) { + var dx, dy, d2, dc = Infinity; + + for (iCell = 0, cover = null; iCell < nCells; ++iCell) { + if (cell = cells[iCell]) { + site = cell.site; + dx = site[0] - x0; + dy = site[1] - y0; + d2 = dx * dx + dy * dy; + if (d2 < dc) dc = d2, cover = cell; + } + } + + if (cover) { + var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0]; + cover.halfedges.push( + edges.push(createBorderEdge(site = cover.site, v00, v01)) - 1, + edges.push(createBorderEdge(site, v01, v11)) - 1, + edges.push(createBorderEdge(site, v11, v10)) - 1, + edges.push(createBorderEdge(site, v10, v00)) - 1 + ); + } + } + + // Lastly delete any cells with no edges; these were entirely clipped. + for (iCell = 0; iCell < nCells; ++iCell) { + if (cell = cells[iCell]) { + if (!cell.halfedges.length) { + delete cells[iCell]; + } + } + } +} + +var circlePool = []; + +var firstCircle; + +function Circle() { + RedBlackNode(this); + this.x = + this.y = + this.arc = + this.site = + this.cy = null; +} + +function attachCircle(arc) { + var lArc = arc.P, + rArc = arc.N; + + if (!lArc || !rArc) return; + + var lSite = lArc.site, + cSite = arc.site, + rSite = rArc.site; + + if (lSite === rSite) return; + + var bx = cSite[0], + by = cSite[1], + ax = lSite[0] - bx, + ay = lSite[1] - by, + cx = rSite[0] - bx, + cy = rSite[1] - by; + + var d = 2 * (ax * cy - ay * cx); + if (d >= -epsilon2$2) return; + + var ha = ax * ax + ay * ay, + hc = cx * cx + cy * cy, + x = (cy * ha - ay * hc) / d, + y = (ax * hc - cx * ha) / d; + + var circle = circlePool.pop() || new Circle; + circle.arc = arc; + circle.site = cSite; + circle.x = x + bx; + circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom + + arc.circle = circle; + + var before = null, + node = circles._; + + while (node) { + if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) { + if (node.L) node = node.L; + else { before = node.P; break; } + } else { + if (node.R) node = node.R; + else { before = node; break; } + } + } + + circles.insert(before, circle); + if (!before) firstCircle = circle; +} + +function detachCircle(arc) { + var circle = arc.circle; + if (circle) { + if (!circle.P) firstCircle = circle.N; + circles.remove(circle); + circlePool.push(circle); + RedBlackNode(circle); + arc.circle = null; + } +} + +var beachPool = []; + +function Beach() { + RedBlackNode(this); + this.edge = + this.site = + this.circle = null; +} + +function createBeach(site) { + var beach = beachPool.pop() || new Beach; + beach.site = site; + return beach; +} + +function detachBeach(beach) { + detachCircle(beach); + beaches.remove(beach); + beachPool.push(beach); + RedBlackNode(beach); +} + +function removeBeach(beach) { + var circle = beach.circle, + x = circle.x, + y = circle.cy, + vertex = [x, y], + previous = beach.P, + next = beach.N, + disappearing = [beach]; + + detachBeach(beach); + + var lArc = previous; + while (lArc.circle + && Math.abs(x - lArc.circle.x) < epsilon$4 + && Math.abs(y - lArc.circle.cy) < epsilon$4) { + previous = lArc.P; + disappearing.unshift(lArc); + detachBeach(lArc); + lArc = previous; + } + + disappearing.unshift(lArc); + detachCircle(lArc); + + var rArc = next; + while (rArc.circle + && Math.abs(x - rArc.circle.x) < epsilon$4 + && Math.abs(y - rArc.circle.cy) < epsilon$4) { + next = rArc.N; + disappearing.push(rArc); + detachBeach(rArc); + rArc = next; + } + + disappearing.push(rArc); + detachCircle(rArc); + + var nArcs = disappearing.length, + iArc; + for (iArc = 1; iArc < nArcs; ++iArc) { + rArc = disappearing[iArc]; + lArc = disappearing[iArc - 1]; + setEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); + } + + lArc = disappearing[0]; + rArc = disappearing[nArcs - 1]; + rArc.edge = createEdge(lArc.site, rArc.site, null, vertex); + + attachCircle(lArc); + attachCircle(rArc); +} + +function addBeach(site) { + var x = site[0], + directrix = site[1], + lArc, + rArc, + dxl, + dxr, + node = beaches._; + + while (node) { + dxl = leftBreakPoint(node, directrix) - x; + if (dxl > epsilon$4) node = node.L; else { + dxr = x - rightBreakPoint(node, directrix); + if (dxr > epsilon$4) { + if (!node.R) { + lArc = node; + break; + } + node = node.R; + } else { + if (dxl > -epsilon$4) { + lArc = node.P; + rArc = node; + } else if (dxr > -epsilon$4) { + lArc = node; + rArc = node.N; + } else { + lArc = rArc = node; + } + break; + } + } + } + + createCell(site); + var newArc = createBeach(site); + beaches.insert(lArc, newArc); + + if (!lArc && !rArc) return; + + if (lArc === rArc) { + detachCircle(lArc); + rArc = createBeach(lArc.site); + beaches.insert(newArc, rArc); + newArc.edge = rArc.edge = createEdge(lArc.site, newArc.site); + attachCircle(lArc); + attachCircle(rArc); + return; + } + + if (!rArc) { // && lArc + newArc.edge = createEdge(lArc.site, newArc.site); + return; + } + + // else lArc !== rArc + detachCircle(lArc); + detachCircle(rArc); + + var lSite = lArc.site, + ax = lSite[0], + ay = lSite[1], + bx = site[0] - ax, + by = site[1] - ay, + rSite = rArc.site, + cx = rSite[0] - ax, + cy = rSite[1] - ay, + d = 2 * (bx * cy - by * cx), + hb = bx * bx + by * by, + hc = cx * cx + cy * cy, + vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay]; + + setEdgeEnd(rArc.edge, lSite, rSite, vertex); + newArc.edge = createEdge(lSite, site, null, vertex); + rArc.edge = createEdge(site, rSite, null, vertex); + attachCircle(lArc); + attachCircle(rArc); +} + +function leftBreakPoint(arc, directrix) { + var site = arc.site, + rfocx = site[0], + rfocy = site[1], + pby2 = rfocy - directrix; + + if (!pby2) return rfocx; + + var lArc = arc.P; + if (!lArc) return -Infinity; + + site = lArc.site; + var lfocx = site[0], + lfocy = site[1], + plby2 = lfocy - directrix; + + if (!plby2) return lfocx; + + var hl = lfocx - rfocx, + aby2 = 1 / pby2 - 1 / plby2, + b = hl / plby2; + + if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; + + return (rfocx + lfocx) / 2; +} + +function rightBreakPoint(arc, directrix) { + var rArc = arc.N; + if (rArc) return leftBreakPoint(rArc, directrix); + var site = arc.site; + return site[1] === directrix ? site[0] : Infinity; +} + +var epsilon$4 = 1e-6; +var epsilon2$2 = 1e-12; +var beaches; +var cells; +var circles; +var edges; + +function triangleArea(a, b, c) { + return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]); +} + +function lexicographic(a, b) { + return b[1] - a[1] + || b[0] - a[0]; +} + +function Diagram(sites, extent) { + var site = sites.sort(lexicographic).pop(), + x, + y, + circle; + + edges = []; + cells = new Array(sites.length); + beaches = new RedBlackTree; + circles = new RedBlackTree; + + while (true) { + circle = firstCircle; + if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) { + if (site[0] !== x || site[1] !== y) { + addBeach(site); + x = site[0], y = site[1]; + } + site = sites.pop(); + } else if (circle) { + removeBeach(circle.arc); + } else { + break; + } + } + + sortCellHalfedges(); + + if (extent) { + var x0 = +extent[0][0], + y0 = +extent[0][1], + x1 = +extent[1][0], + y1 = +extent[1][1]; + clipEdges(x0, y0, x1, y1); + clipCells(x0, y0, x1, y1); + } + + this.edges = edges; + this.cells = cells; + + beaches = + circles = + edges = + cells = null; +} + +Diagram.prototype = { + constructor: Diagram, + + polygons: function() { + var edges = this.edges; + + return this.cells.map(function(cell) { + var polygon = cell.halfedges.map(function(i) { return cellHalfedgeStart(cell, edges[i]); }); + polygon.data = cell.site.data; + return polygon; + }); + }, + + triangles: function() { + var triangles = [], + edges = this.edges; + + this.cells.forEach(function(cell, i) { + if (!(m = (halfedges = cell.halfedges).length)) return; + var site = cell.site, + halfedges, + j = -1, + m, + s0, + e1 = edges[halfedges[m - 1]], + s1 = e1.left === site ? e1.right : e1.left; + + while (++j < m) { + s0 = s1; + e1 = edges[halfedges[j]]; + s1 = e1.left === site ? e1.right : e1.left; + if (s0 && s1 && i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) { + triangles.push([site.data, s0.data, s1.data]); + } + } + }); + + return triangles; + }, + + links: function() { + return this.edges.filter(function(edge) { + return edge.right; + }).map(function(edge) { + return { + source: edge.left.data, + target: edge.right.data + }; + }); + }, + + find: function(x, y, radius) { + var that = this, i0, i1 = that._found || 0, n = that.cells.length, cell; + + // Use the previously-found cell, or start with an arbitrary one. + while (!(cell = that.cells[i1])) if (++i1 >= n) return null; + var dx = x - cell.site[0], dy = y - cell.site[1], d2 = dx * dx + dy * dy; + + // Traverse the half-edges to find a closer cell, if any. + do { + cell = that.cells[i0 = i1], i1 = null; + cell.halfedges.forEach(function(e) { + var edge = that.edges[e], v = edge.left; + if ((v === cell.site || !v) && !(v = edge.right)) return; + var vx = x - v[0], vy = y - v[1], v2 = vx * vx + vy * vy; + if (v2 < d2) d2 = v2, i1 = v.index; + }); + } while (i1 !== null); + + that._found = i0; + + return radius == null || d2 <= radius * radius ? cell.site : null; + } +}; + +function voronoi() { + var x$$1 = x$4, + y$$1 = y$4, + extent = null; + + function voronoi(data) { + return new Diagram(data.map(function(d, i) { + var s = [Math.round(x$$1(d, i, data) / epsilon$4) * epsilon$4, Math.round(y$$1(d, i, data) / epsilon$4) * epsilon$4]; + s.index = i; + s.data = d; + return s; + }), extent); + } + + voronoi.polygons = function(data) { + return voronoi(data).polygons(); + }; + + voronoi.links = function(data) { + return voronoi(data).links(); + }; + + voronoi.triangles = function(data) { + return voronoi(data).triangles(); + }; + + voronoi.x = function(_) { + return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), voronoi) : x$$1; + }; + + voronoi.y = function(_) { + return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), voronoi) : y$$1; + }; + + voronoi.extent = function(_) { + return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]]; + }; + + voronoi.size = function(_) { + return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]]; + }; + + return voronoi; +} + +function constant$12(x) { + return function() { + return x; + }; +} + +function ZoomEvent(target, type, transform) { + this.target = target; + this.type = type; + this.transform = transform; +} + +function Transform(k, x, y) { + this.k = k; + this.x = x; + this.y = y; +} + +Transform.prototype = { + constructor: Transform, + scale: function(k) { + return k === 1 ? this : new Transform(this.k * k, this.x, this.y); + }, + translate: function(x, y) { + return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y); + }, + apply: function(point) { + return [point[0] * this.k + this.x, point[1] * this.k + this.y]; + }, + applyX: function(x) { + return x * this.k + this.x; + }, + applyY: function(y) { + return y * this.k + this.y; + }, + invert: function(location) { + return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k]; + }, + invertX: function(x) { + return (x - this.x) / this.k; + }, + invertY: function(y) { + return (y - this.y) / this.k; + }, + rescaleX: function(x) { + return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x)); + }, + rescaleY: function(y) { + return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y)); + }, + toString: function() { + return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")"; + } +}; + +var identity$8 = new Transform(1, 0, 0); + +transform$1.prototype = Transform.prototype; + +function transform$1(node) { + return node.__zoom || identity$8; +} + +function nopropagation$2() { + exports.event.stopImmediatePropagation(); +} + +function noevent$2() { + exports.event.preventDefault(); + exports.event.stopImmediatePropagation(); +} + +// Ignore right-click, since that should open the context menu. +function defaultFilter$2() { + return !exports.event.button; +} + +function defaultExtent$1() { + var e = this, w, h; + if (e instanceof SVGElement) { + e = e.ownerSVGElement || e; + w = e.width.baseVal.value; + h = e.height.baseVal.value; + } else { + w = e.clientWidth; + h = e.clientHeight; + } + return [[0, 0], [w, h]]; +} + +function defaultTransform() { + return this.__zoom || identity$8; +} + +function defaultWheelDelta() { + return -exports.event.deltaY * (exports.event.deltaMode ? 120 : 1) / 500; +} + +function defaultTouchable$1() { + return "ontouchstart" in this; +} + +function defaultConstrain(transform$$1, extent, translateExtent) { + var dx0 = transform$$1.invertX(extent[0][0]) - translateExtent[0][0], + dx1 = transform$$1.invertX(extent[1][0]) - translateExtent[1][0], + dy0 = transform$$1.invertY(extent[0][1]) - translateExtent[0][1], + dy1 = transform$$1.invertY(extent[1][1]) - translateExtent[1][1]; + return transform$$1.translate( + dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), + dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1) + ); +} + +function zoom() { + var filter = defaultFilter$2, + extent = defaultExtent$1, + constrain = defaultConstrain, + wheelDelta = defaultWheelDelta, + touchable = defaultTouchable$1, + scaleExtent = [0, Infinity], + translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], + duration = 250, + interpolate = interpolateZoom, + gestures = [], + listeners = dispatch("start", "zoom", "end"), + touchstarting, + touchending, + touchDelay = 500, + wheelDelay = 150, + clickDistance2 = 0; + + function zoom(selection) { + selection + .property("__zoom", defaultTransform) + .on("wheel.zoom", wheeled) + .on("mousedown.zoom", mousedowned) + .on("dblclick.zoom", dblclicked) + .filter(touchable) + .on("touchstart.zoom", touchstarted) + .on("touchmove.zoom", touchmoved) + .on("touchend.zoom touchcancel.zoom", touchended) + .style("touch-action", "none") + .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); + } + + zoom.transform = function(collection, transform$$1) { + var selection = collection.selection ? collection.selection() : collection; + selection.property("__zoom", defaultTransform); + if (collection !== selection) { + schedule(collection, transform$$1); + } else { + selection.interrupt().each(function() { + gesture(this, arguments) + .start() + .zoom(null, typeof transform$$1 === "function" ? transform$$1.apply(this, arguments) : transform$$1) + .end(); + }); + } + }; + + zoom.scaleBy = function(selection, k) { + zoom.scaleTo(selection, function() { + var k0 = this.__zoom.k, + k1 = typeof k === "function" ? k.apply(this, arguments) : k; + return k0 * k1; + }); + }; + + zoom.scaleTo = function(selection, k) { + zoom.transform(selection, function() { + var e = extent.apply(this, arguments), + t0 = this.__zoom, + p0 = centroid(e), + p1 = t0.invert(p0), + k1 = typeof k === "function" ? k.apply(this, arguments) : k; + return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent); + }); + }; + + zoom.translateBy = function(selection, x, y) { + zoom.transform(selection, function() { + return constrain(this.__zoom.translate( + typeof x === "function" ? x.apply(this, arguments) : x, + typeof y === "function" ? y.apply(this, arguments) : y + ), extent.apply(this, arguments), translateExtent); + }); + }; + + zoom.translateTo = function(selection, x, y) { + zoom.transform(selection, function() { + var e = extent.apply(this, arguments), + t = this.__zoom, + p = centroid(e); + return constrain(identity$8.translate(p[0], p[1]).scale(t.k).translate( + typeof x === "function" ? -x.apply(this, arguments) : -x, + typeof y === "function" ? -y.apply(this, arguments) : -y + ), e, translateExtent); + }); + }; + + function scale(transform$$1, k) { + k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k)); + return k === transform$$1.k ? transform$$1 : new Transform(k, transform$$1.x, transform$$1.y); + } + + function translate(transform$$1, p0, p1) { + var x = p0[0] - p1[0] * transform$$1.k, y = p0[1] - p1[1] * transform$$1.k; + return x === transform$$1.x && y === transform$$1.y ? transform$$1 : new Transform(transform$$1.k, x, y); + } + + function centroid(extent) { + return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2]; + } + + function schedule(transition, transform$$1, center) { + transition + .on("start.zoom", function() { gesture(this, arguments).start(); }) + .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); }) + .tween("zoom", function() { + var that = this, + args = arguments, + g = gesture(that, args), + e = extent.apply(that, args), + p = center || centroid(e), + w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), + a = that.__zoom, + b = typeof transform$$1 === "function" ? transform$$1.apply(that, args) : transform$$1, + i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k)); + return function(t) { + if (t === 1) t = b; // Avoid rounding error on end. + else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); } + g.zoom(null, t); + }; + }); + } + + function gesture(that, args) { + for (var i = 0, n = gestures.length, g; i < n; ++i) { + if ((g = gestures[i]).that === that) { + return g; + } + } + return new Gesture(that, args); + } + + function Gesture(that, args) { + this.that = that; + this.args = args; + this.index = -1; + this.active = 0; + this.extent = extent.apply(that, args); + } + + Gesture.prototype = { + start: function() { + if (++this.active === 1) { + this.index = gestures.push(this) - 1; + this.emit("start"); + } + return this; + }, + zoom: function(key, transform$$1) { + if (this.mouse && key !== "mouse") this.mouse[1] = transform$$1.invert(this.mouse[0]); + if (this.touch0 && key !== "touch") this.touch0[1] = transform$$1.invert(this.touch0[0]); + if (this.touch1 && key !== "touch") this.touch1[1] = transform$$1.invert(this.touch1[0]); + this.that.__zoom = transform$$1; + this.emit("zoom"); + return this; + }, + end: function() { + if (--this.active === 0) { + gestures.splice(this.index, 1); + this.index = -1; + this.emit("end"); + } + return this; + }, + emit: function(type) { + customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]); + } + }; + + function wheeled() { + if (!filter.apply(this, arguments)) return; + var g = gesture(this, arguments), + t = this.__zoom, + k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))), + p = mouse(this); + + // If the mouse is in the same location as before, reuse it. + // If there were recent wheel events, reset the wheel idle timeout. + if (g.wheel) { + if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { + g.mouse[1] = t.invert(g.mouse[0] = p); + } + clearTimeout(g.wheel); + } + + // If this wheel event won’t trigger a transform change, ignore it. + else if (t.k === k) return; + + // Otherwise, capture the mouse point and location at the start. + else { + g.mouse = [p, t.invert(p)]; + interrupt(this); + g.start(); + } + + noevent$2(); + g.wheel = setTimeout(wheelidled, wheelDelay); + g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent)); + + function wheelidled() { + g.wheel = null; + g.end(); + } + } + + function mousedowned() { + if (touchending || !filter.apply(this, arguments)) return; + var g = gesture(this, arguments), + v = select(exports.event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true), + p = mouse(this), + x0 = exports.event.clientX, + y0 = exports.event.clientY; + + dragDisable(exports.event.view); + nopropagation$2(); + g.mouse = [p, this.__zoom.invert(p)]; + interrupt(this); + g.start(); + + function mousemoved() { + noevent$2(); + if (!g.moved) { + var dx = exports.event.clientX - x0, dy = exports.event.clientY - y0; + g.moved = dx * dx + dy * dy > clickDistance2; + } + g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent, translateExtent)); + } + + function mouseupped() { + v.on("mousemove.zoom mouseup.zoom", null); + yesdrag(exports.event.view, g.moved); + noevent$2(); + g.end(); + } + } + + function dblclicked() { + if (!filter.apply(this, arguments)) return; + var t0 = this.__zoom, + p0 = mouse(this), + p1 = t0.invert(p0), + k1 = t0.k * (exports.event.shiftKey ? 0.5 : 2), + t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent); + + noevent$2(); + if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0); + else select(this).call(zoom.transform, t1); + } + + function touchstarted() { + if (!filter.apply(this, arguments)) return; + var g = gesture(this, arguments), + touches = exports.event.changedTouches, + started, + n = touches.length, i, t, p; + + nopropagation$2(); + for (i = 0; i < n; ++i) { + t = touches[i], p = touch(this, touches, t.identifier); + p = [p, this.__zoom.invert(p), t.identifier]; + if (!g.touch0) g.touch0 = p, started = true; + else if (!g.touch1) g.touch1 = p; + } + + // If this is a dbltap, reroute to the (optional) dblclick.zoom handler. + if (touchstarting) { + touchstarting = clearTimeout(touchstarting); + if (!g.touch1) { + g.end(); + p = select(this).on("dblclick.zoom"); + if (p) p.apply(this, arguments); + return; + } + } + + if (started) { + touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay); + interrupt(this); + g.start(); + } + } + + function touchmoved() { + var g = gesture(this, arguments), + touches = exports.event.changedTouches, + n = touches.length, i, t, p, l; + + noevent$2(); + if (touchstarting) touchstarting = clearTimeout(touchstarting); + for (i = 0; i < n; ++i) { + t = touches[i], p = touch(this, touches, t.identifier); + if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p; + else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p; + } + t = g.that.__zoom; + if (g.touch1) { + var p0 = g.touch0[0], l0 = g.touch0[1], + p1 = g.touch1[0], l1 = g.touch1[1], + dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, + dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl; + t = scale(t, Math.sqrt(dp / dl)); + p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; + l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; + } + else if (g.touch0) p = g.touch0[0], l = g.touch0[1]; + else return; + g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent)); + } + + function touchended() { + var g = gesture(this, arguments), + touches = exports.event.changedTouches, + n = touches.length, i, t; + + nopropagation$2(); + if (touchending) clearTimeout(touchending); + touchending = setTimeout(function() { touchending = null; }, touchDelay); + for (i = 0; i < n; ++i) { + t = touches[i]; + if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0; + else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1; + } + if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1; + if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]); + else g.end(); + } + + zoom.wheelDelta = function(_) { + return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$12(+_), zoom) : wheelDelta; + }; + + zoom.filter = function(_) { + return arguments.length ? (filter = typeof _ === "function" ? _ : constant$12(!!_), zoom) : filter; + }; + + zoom.touchable = function(_) { + return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$12(!!_), zoom) : touchable; + }; + + zoom.extent = function(_) { + return arguments.length ? (extent = typeof _ === "function" ? _ : constant$12([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; + }; + + zoom.scaleExtent = function(_) { + return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]]; + }; + + zoom.translateExtent = function(_) { + return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]]; + }; + + zoom.constrain = function(_) { + return arguments.length ? (constrain = _, zoom) : constrain; + }; + + zoom.duration = function(_) { + return arguments.length ? (duration = +_, zoom) : duration; + }; + + zoom.interpolate = function(_) { + return arguments.length ? (interpolate = _, zoom) : interpolate; + }; + + zoom.on = function() { + var value = listeners.on.apply(listeners, arguments); + return value === listeners ? zoom : value; + }; + + zoom.clickDistance = function(_) { + return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2); + }; + + return zoom; +} + +exports.version = version; +exports.bisect = bisectRight; +exports.bisectRight = bisectRight; +exports.bisectLeft = bisectLeft; +exports.ascending = ascending; +exports.bisector = bisector; +exports.cross = cross; +exports.descending = descending; +exports.deviation = deviation; +exports.extent = extent; +exports.histogram = histogram; +exports.thresholdFreedmanDiaconis = freedmanDiaconis; +exports.thresholdScott = scott; +exports.thresholdSturges = sturges; +exports.max = max; +exports.mean = mean; +exports.median = median; +exports.merge = merge; +exports.min = min; +exports.pairs = pairs; +exports.permute = permute; +exports.quantile = threshold; +exports.range = sequence; +exports.scan = scan; +exports.shuffle = shuffle; +exports.sum = sum; +exports.ticks = ticks; +exports.tickIncrement = tickIncrement; +exports.tickStep = tickStep; +exports.transpose = transpose; +exports.variance = variance; +exports.zip = zip; +exports.axisTop = axisTop; +exports.axisRight = axisRight; +exports.axisBottom = axisBottom; +exports.axisLeft = axisLeft; +exports.brush = brush; +exports.brushX = brushX; +exports.brushY = brushY; +exports.brushSelection = brushSelection; +exports.chord = chord; +exports.ribbon = ribbon; +exports.nest = nest; +exports.set = set$2; +exports.map = map$1; +exports.keys = keys; +exports.values = values; +exports.entries = entries; +exports.color = color; +exports.rgb = rgb; +exports.hsl = hsl; +exports.lab = lab; +exports.hcl = hcl; +exports.cubehelix = cubehelix; +exports.dispatch = dispatch; +exports.drag = drag; +exports.dragDisable = dragDisable; +exports.dragEnable = yesdrag; +exports.dsvFormat = dsv; +exports.csvParse = csvParse; +exports.csvParseRows = csvParseRows; +exports.csvFormat = csvFormat; +exports.csvFormatRows = csvFormatRows; +exports.tsvParse = tsvParse; +exports.tsvParseRows = tsvParseRows; +exports.tsvFormat = tsvFormat; +exports.tsvFormatRows = tsvFormatRows; +exports.easeLinear = linear$1; +exports.easeQuad = quadInOut; +exports.easeQuadIn = quadIn; +exports.easeQuadOut = quadOut; +exports.easeQuadInOut = quadInOut; +exports.easeCubic = cubicInOut; +exports.easeCubicIn = cubicIn; +exports.easeCubicOut = cubicOut; +exports.easeCubicInOut = cubicInOut; +exports.easePoly = polyInOut; +exports.easePolyIn = polyIn; +exports.easePolyOut = polyOut; +exports.easePolyInOut = polyInOut; +exports.easeSin = sinInOut; +exports.easeSinIn = sinIn; +exports.easeSinOut = sinOut; +exports.easeSinInOut = sinInOut; +exports.easeExp = expInOut; +exports.easeExpIn = expIn; +exports.easeExpOut = expOut; +exports.easeExpInOut = expInOut; +exports.easeCircle = circleInOut; +exports.easeCircleIn = circleIn; +exports.easeCircleOut = circleOut; +exports.easeCircleInOut = circleInOut; +exports.easeBounce = bounceOut; +exports.easeBounceIn = bounceIn; +exports.easeBounceOut = bounceOut; +exports.easeBounceInOut = bounceInOut; +exports.easeBack = backInOut; +exports.easeBackIn = backIn; +exports.easeBackOut = backOut; +exports.easeBackInOut = backInOut; +exports.easeElastic = elasticOut; +exports.easeElasticIn = elasticIn; +exports.easeElasticOut = elasticOut; +exports.easeElasticInOut = elasticInOut; +exports.forceCenter = center$1; +exports.forceCollide = collide; +exports.forceLink = link; +exports.forceManyBody = manyBody; +exports.forceRadial = radial; +exports.forceSimulation = simulation; +exports.forceX = x$2; +exports.forceY = y$2; +exports.formatDefaultLocale = defaultLocale; +exports.formatLocale = formatLocale; +exports.formatSpecifier = formatSpecifier; +exports.precisionFixed = precisionFixed; +exports.precisionPrefix = precisionPrefix; +exports.precisionRound = precisionRound; +exports.geoArea = area; +exports.geoBounds = bounds; +exports.geoCentroid = centroid; +exports.geoCircle = circle; +exports.geoClipAntimeridian = clipAntimeridian; +exports.geoClipCircle = clipCircle; +exports.geoClipExtent = extent$1; +exports.geoClipRectangle = clipRectangle; +exports.geoContains = contains; +exports.geoDistance = distance; +exports.geoGraticule = graticule; +exports.geoGraticule10 = graticule10; +exports.geoInterpolate = interpolate$1; +exports.geoLength = length$1; +exports.geoPath = index$1; +exports.geoAlbers = albers; +exports.geoAlbersUsa = albersUsa; +exports.geoAzimuthalEqualArea = azimuthalEqualArea; +exports.geoAzimuthalEqualAreaRaw = azimuthalEqualAreaRaw; +exports.geoAzimuthalEquidistant = azimuthalEquidistant; +exports.geoAzimuthalEquidistantRaw = azimuthalEquidistantRaw; +exports.geoConicConformal = conicConformal; +exports.geoConicConformalRaw = conicConformalRaw; +exports.geoConicEqualArea = conicEqualArea; +exports.geoConicEqualAreaRaw = conicEqualAreaRaw; +exports.geoConicEquidistant = conicEquidistant; +exports.geoConicEquidistantRaw = conicEquidistantRaw; +exports.geoEquirectangular = equirectangular; +exports.geoEquirectangularRaw = equirectangularRaw; +exports.geoGnomonic = gnomonic; +exports.geoGnomonicRaw = gnomonicRaw; +exports.geoIdentity = identity$5; +exports.geoProjection = projection; +exports.geoProjectionMutator = projectionMutator; +exports.geoMercator = mercator; +exports.geoMercatorRaw = mercatorRaw; +exports.geoNaturalEarth1 = naturalEarth1; +exports.geoNaturalEarth1Raw = naturalEarth1Raw; +exports.geoOrthographic = orthographic; +exports.geoOrthographicRaw = orthographicRaw; +exports.geoStereographic = stereographic; +exports.geoStereographicRaw = stereographicRaw; +exports.geoTransverseMercator = transverseMercator; +exports.geoTransverseMercatorRaw = transverseMercatorRaw; +exports.geoRotation = rotation; +exports.geoStream = geoStream; +exports.geoTransform = transform; +exports.cluster = cluster; +exports.hierarchy = hierarchy; +exports.pack = index$2; +exports.packSiblings = siblings; +exports.packEnclose = enclose; +exports.partition = partition; +exports.stratify = stratify; +exports.tree = tree; +exports.treemap = index$3; +exports.treemapBinary = binary; +exports.treemapDice = treemapDice; +exports.treemapSlice = treemapSlice; +exports.treemapSliceDice = sliceDice; +exports.treemapSquarify = squarify; +exports.treemapResquarify = resquarify; +exports.interpolate = interpolateValue; +exports.interpolateArray = array$1; +exports.interpolateBasis = basis$1; +exports.interpolateBasisClosed = basisClosed; +exports.interpolateDate = date; +exports.interpolateNumber = reinterpolate; +exports.interpolateObject = object; +exports.interpolateRound = interpolateRound; +exports.interpolateString = interpolateString; +exports.interpolateTransformCss = interpolateTransformCss; +exports.interpolateTransformSvg = interpolateTransformSvg; +exports.interpolateZoom = interpolateZoom; +exports.interpolateRgb = interpolateRgb; +exports.interpolateRgbBasis = rgbBasis; +exports.interpolateRgbBasisClosed = rgbBasisClosed; +exports.interpolateHsl = hsl$2; +exports.interpolateHslLong = hslLong; +exports.interpolateLab = lab$1; +exports.interpolateHcl = hcl$2; +exports.interpolateHclLong = hclLong; +exports.interpolateCubehelix = cubehelix$2; +exports.interpolateCubehelixLong = cubehelixLong; +exports.quantize = quantize; +exports.path = path; +exports.polygonArea = area$1; +exports.polygonCentroid = centroid$1; +exports.polygonHull = hull; +exports.polygonContains = contains$1; +exports.polygonLength = length$2; +exports.quadtree = quadtree; +exports.queue = queue; +exports.randomUniform = uniform; +exports.randomNormal = normal; +exports.randomLogNormal = logNormal; +exports.randomBates = bates; +exports.randomIrwinHall = irwinHall; +exports.randomExponential = exponential$1; +exports.request = request; +exports.html = html; +exports.json = json; +exports.text = text; +exports.xml = xml; +exports.csv = csv$1; +exports.tsv = tsv$1; +exports.scaleBand = band; +exports.scalePoint = point$1; +exports.scaleIdentity = identity$6; +exports.scaleLinear = linear$2; +exports.scaleLog = log$1; +exports.scaleOrdinal = ordinal; +exports.scaleImplicit = implicit; +exports.scalePow = pow$1; +exports.scaleSqrt = sqrt$1; +exports.scaleQuantile = quantile$$1; +exports.scaleQuantize = quantize$1; +exports.scaleThreshold = threshold$1; +exports.scaleTime = time; +exports.scaleUtc = utcTime; +exports.schemeCategory10 = category10; +exports.schemeCategory20b = category20b; +exports.schemeCategory20c = category20c; +exports.schemeCategory20 = category20; +exports.interpolateCubehelixDefault = cubehelix$3; +exports.interpolateRainbow = rainbow$1; +exports.interpolateWarm = warm; +exports.interpolateCool = cool; +exports.interpolateViridis = viridis; +exports.interpolateMagma = magma; +exports.interpolateInferno = inferno; +exports.interpolatePlasma = plasma; +exports.scaleSequential = sequential; +exports.creator = creator; +exports.local = local$1; +exports.matcher = matcher$1; +exports.mouse = mouse; +exports.namespace = namespace; +exports.namespaces = namespaces; +exports.clientPoint = point; +exports.select = select; +exports.selectAll = selectAll; +exports.selection = selection; +exports.selector = selector; +exports.selectorAll = selectorAll; +exports.style = styleValue; +exports.touch = touch; +exports.touches = touches; +exports.window = defaultView; +exports.customEvent = customEvent; +exports.arc = arc; +exports.area = area$2; +exports.line = line; +exports.pie = pie; +exports.areaRadial = areaRadial; +exports.radialArea = areaRadial; +exports.lineRadial = lineRadial$1; +exports.radialLine = lineRadial$1; +exports.pointRadial = pointRadial; +exports.linkHorizontal = linkHorizontal; +exports.linkVertical = linkVertical; +exports.linkRadial = linkRadial; +exports.symbol = symbol; +exports.symbols = symbols; +exports.symbolCircle = circle$2; +exports.symbolCross = cross$2; +exports.symbolDiamond = diamond; +exports.symbolSquare = square; +exports.symbolStar = star; +exports.symbolTriangle = triangle; +exports.symbolWye = wye; +exports.curveBasisClosed = basisClosed$1; +exports.curveBasisOpen = basisOpen; +exports.curveBasis = basis$2; +exports.curveBundle = bundle; +exports.curveCardinalClosed = cardinalClosed; +exports.curveCardinalOpen = cardinalOpen; +exports.curveCardinal = cardinal; +exports.curveCatmullRomClosed = catmullRomClosed; +exports.curveCatmullRomOpen = catmullRomOpen; +exports.curveCatmullRom = catmullRom; +exports.curveLinearClosed = linearClosed; +exports.curveLinear = curveLinear; +exports.curveMonotoneX = monotoneX; +exports.curveMonotoneY = monotoneY; +exports.curveNatural = natural; +exports.curveStep = step; +exports.curveStepAfter = stepAfter; +exports.curveStepBefore = stepBefore; +exports.stack = stack; +exports.stackOffsetExpand = expand; +exports.stackOffsetDiverging = diverging; +exports.stackOffsetNone = none$1; +exports.stackOffsetSilhouette = silhouette; +exports.stackOffsetWiggle = wiggle; +exports.stackOrderAscending = ascending$2; +exports.stackOrderDescending = descending$2; +exports.stackOrderInsideOut = insideOut; +exports.stackOrderNone = none$2; +exports.stackOrderReverse = reverse; +exports.timeInterval = newInterval; +exports.timeMillisecond = millisecond; +exports.timeMilliseconds = milliseconds; +exports.utcMillisecond = millisecond; +exports.utcMilliseconds = milliseconds; +exports.timeSecond = second; +exports.timeSeconds = seconds; +exports.utcSecond = second; +exports.utcSeconds = seconds; +exports.timeMinute = minute; +exports.timeMinutes = minutes; +exports.timeHour = hour; +exports.timeHours = hours; +exports.timeDay = day; +exports.timeDays = days; +exports.timeWeek = sunday; +exports.timeWeeks = sundays; +exports.timeSunday = sunday; +exports.timeSundays = sundays; +exports.timeMonday = monday; +exports.timeMondays = mondays; +exports.timeTuesday = tuesday; +exports.timeTuesdays = tuesdays; +exports.timeWednesday = wednesday; +exports.timeWednesdays = wednesdays; +exports.timeThursday = thursday; +exports.timeThursdays = thursdays; +exports.timeFriday = friday; +exports.timeFridays = fridays; +exports.timeSaturday = saturday; +exports.timeSaturdays = saturdays; +exports.timeMonth = month; +exports.timeMonths = months; +exports.timeYear = year; +exports.timeYears = years; +exports.utcMinute = utcMinute; +exports.utcMinutes = utcMinutes; +exports.utcHour = utcHour; +exports.utcHours = utcHours; +exports.utcDay = utcDay; +exports.utcDays = utcDays; +exports.utcWeek = utcSunday; +exports.utcWeeks = utcSundays; +exports.utcSunday = utcSunday; +exports.utcSundays = utcSundays; +exports.utcMonday = utcMonday; +exports.utcMondays = utcMondays; +exports.utcTuesday = utcTuesday; +exports.utcTuesdays = utcTuesdays; +exports.utcWednesday = utcWednesday; +exports.utcWednesdays = utcWednesdays; +exports.utcThursday = utcThursday; +exports.utcThursdays = utcThursdays; +exports.utcFriday = utcFriday; +exports.utcFridays = utcFridays; +exports.utcSaturday = utcSaturday; +exports.utcSaturdays = utcSaturdays; +exports.utcMonth = utcMonth; +exports.utcMonths = utcMonths; +exports.utcYear = utcYear; +exports.utcYears = utcYears; +exports.timeFormatDefaultLocale = defaultLocale$1; +exports.timeFormatLocale = formatLocale$1; +exports.isoFormat = formatIso; +exports.isoParse = parseIso; +exports.now = now; +exports.timer = timer; +exports.timerFlush = timerFlush; +exports.timeout = timeout$1; +exports.interval = interval$1; +exports.transition = transition; +exports.active = active; +exports.interrupt = interrupt; +exports.voronoi = voronoi; +exports.zoom = zoom; +exports.zoomTransform = transform$1; +exports.zoomIdentity = identity$8; + +Object.defineProperty(exports, '__esModule', { value: true }); + +}))); diff --git a/debian/missing-sources/dygraph-c91c859.js b/debian/missing-sources/dygraph-c91c859.js new file mode 100644 index 000000000..2565b2213 --- /dev/null +++ b/debian/missing-sources/dygraph-c91c859.js @@ -0,0 +1,3482 @@ +/** + * @license + * Copyright 2006 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview Creates an interactive, zoomable graph based on a CSV file or + * string. Dygraph can handle multiple series with or without error bars. The + * date/value ranges will be automatically set. Dygraph uses the + * <canvas> tag, so it only works in FF1.5+. + * @author danvdk@gmail.com (Dan Vanderkam) + + Usage: +
+ + + The CSV file is of the form + + Date,SeriesA,SeriesB,SeriesC + YYYYMMDD,A1,B1,C1 + YYYYMMDD,A2,B2,C2 + + If the 'errorBars' option is set in the constructor, the input should be of + the form + Date,SeriesA,SeriesB,... + YYYYMMDD,A1,sigmaA1,B1,sigmaB1,... + YYYYMMDD,A2,sigmaA2,B2,sigmaB2,... + + If the 'fractions' option is set, the input should be of the form: + + Date,SeriesA,SeriesB,... + YYYYMMDD,A1/B1,A2/B2,... + YYYYMMDD,A1/B1,A2/B2,... + + And error bars will be calculated automatically using a binomial distribution. + + For further documentation and examples, see http://dygraphs.com/ + */ + +import DygraphLayout from './dygraph-layout'; +import DygraphCanvasRenderer from './dygraph-canvas'; +import DygraphOptions from './dygraph-options'; +import DygraphInteraction from './dygraph-interaction-model'; +import * as DygraphTickers from './dygraph-tickers'; +import * as utils from './dygraph-utils'; +import DEFAULT_ATTRS from './dygraph-default-attrs'; +import OPTIONS_REFERENCE from './dygraph-options-reference'; +import IFrameTarp from './iframe-tarp'; + +import DefaultHandler from './datahandler/default'; +import ErrorBarsHandler from './datahandler/bars-error'; +import CustomBarsHandler from './datahandler/bars-custom'; +import DefaultFractionHandler from './datahandler/default-fractions'; +import FractionsBarsHandler from './datahandler/bars-fractions'; +import BarsHandler from './datahandler/bars'; + +import AnnotationsPlugin from './plugins/annotations'; +import AxesPlugin from './plugins/axes'; +import ChartLabelsPlugin from './plugins/chart-labels'; +import GridPlugin from './plugins/grid'; +import LegendPlugin from './plugins/legend'; +import RangeSelectorPlugin from './plugins/range-selector'; + +import GVizChart from './dygraph-gviz'; + +"use strict"; + +/** + * Creates an interactive, zoomable chart. + * + * @constructor + * @param {div | String} div A div or the id of a div into which to construct + * the chart. + * @param {String | Function} file A file containing CSV data or a function + * that returns this data. The most basic expected format for each line is + * "YYYY/MM/DD,val1,val2,...". For more information, see + * http://dygraphs.com/data.html. + * @param {Object} attrs Various other attributes, e.g. errorBars determines + * whether the input data contains error ranges. For a complete list of + * options, see http://dygraphs.com/options.html. + */ +var Dygraph = function(div, data, opts) { + this.__init__(div, data, opts); +}; + +Dygraph.NAME = "Dygraph"; +Dygraph.VERSION = "2.1.0"; + +// Various default values +Dygraph.DEFAULT_ROLL_PERIOD = 1; +Dygraph.DEFAULT_WIDTH = 480; +Dygraph.DEFAULT_HEIGHT = 320; + +// For max 60 Hz. animation: +Dygraph.ANIMATION_STEPS = 12; +Dygraph.ANIMATION_DURATION = 200; + +/** + * Standard plotters. These may be used by clients. + * Available plotters are: + * - Dygraph.Plotters.linePlotter: draws central lines (most common) + * - Dygraph.Plotters.errorPlotter: draws error bars + * - Dygraph.Plotters.fillPlotter: draws fills under lines (used with fillGraph) + * + * By default, the plotter is [fillPlotter, errorPlotter, linePlotter]. + * This causes all the lines to be drawn over all the fills/error bars. + */ +Dygraph.Plotters = DygraphCanvasRenderer._Plotters; + + +// Used for initializing annotation CSS rules only once. +Dygraph.addedAnnotationCSS = false; + +/** + * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit + * and context <canvas> inside of it. See the constructor for details. + * on the parameters. + * @param {Element} div the Element to render the graph into. + * @param {string | Function} file Source data + * @param {Object} attrs Miscellaneous other options + * @private + */ +Dygraph.prototype.__init__ = function(div, file, attrs) { + this.is_initial_draw_ = true; + this.readyFns_ = []; + + // Support two-argument constructor + if (attrs === null || attrs === undefined) { attrs = {}; } + + attrs = Dygraph.copyUserAttrs_(attrs); + + if (typeof(div) == 'string') { + div = document.getElementById(div); + } + + if (!div) { + throw new Error('Constructing dygraph with a non-existent div!'); + } + + // Copy the important bits into the object + // TODO(danvk): most of these should just stay in the attrs_ dictionary. + this.maindiv_ = div; + this.file_ = file; + this.rollPeriod_ = attrs.rollPeriod || Dygraph.DEFAULT_ROLL_PERIOD; + this.previousVerticalX_ = -1; + this.fractions_ = attrs.fractions || false; + this.dateWindow_ = attrs.dateWindow || null; + + this.annotations_ = []; + + // Clear the div. This ensure that, if multiple dygraphs are passed the same + // div, then only one will be drawn. + div.innerHTML = ""; + + // For historical reasons, the 'width' and 'height' options trump all CSS + // rules _except_ for an explicit 'width' or 'height' on the div. + // As an added convenience, if the div has zero height (like
does + // without any styles), then we use a default height/width. + if (div.style.width === '' && attrs.width) { + div.style.width = attrs.width + "px"; + } + if (div.style.height === '' && attrs.height) { + div.style.height = attrs.height + "px"; + } + if (div.style.height === '' && div.clientHeight === 0) { + div.style.height = Dygraph.DEFAULT_HEIGHT + "px"; + if (div.style.width === '') { + div.style.width = Dygraph.DEFAULT_WIDTH + "px"; + } + } + // These will be zero if the dygraph's div is hidden. In that case, + // use the user-specified attributes if present. If not, use zero + // and assume the user will call resize to fix things later. + this.width_ = div.clientWidth || attrs.width || 0; + this.height_ = div.clientHeight || attrs.height || 0; + + // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_. + if (attrs.stackedGraph) { + attrs.fillGraph = true; + // TODO(nikhilk): Add any other stackedGraph checks here. + } + + // DEPRECATION WARNING: All option processing should be moved from + // attrs_ and user_attrs_ to options_, which holds all this information. + // + // Dygraphs has many options, some of which interact with one another. + // To keep track of everything, we maintain two sets of options: + // + // this.user_attrs_ only options explicitly set by the user. + // this.attrs_ defaults, options derived from user_attrs_, data. + // + // Options are then accessed this.attr_('attr'), which first looks at + // user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent + // defaults without overriding behavior that the user specifically asks for. + this.user_attrs_ = {}; + utils.update(this.user_attrs_, attrs); + + // This sequence ensures that Dygraph.DEFAULT_ATTRS is never modified. + this.attrs_ = {}; + utils.updateDeep(this.attrs_, DEFAULT_ATTRS); + + this.boundaryIds_ = []; + this.setIndexByName_ = {}; + this.datasetIndex_ = []; + + this.registeredEvents_ = []; + this.eventListeners_ = {}; + + this.attributes_ = new DygraphOptions(this); + + // Create the containing DIV and other interactive elements + this.createInterface_(); + + // Activate plugins. + this.plugins_ = []; + var plugins = Dygraph.PLUGINS.concat(this.getOption('plugins')); + for (var i = 0; i < plugins.length; i++) { + // the plugins option may contain either plugin classes or instances. + // Plugin instances contain an activate method. + var Plugin = plugins[i]; // either a constructor or an instance. + var pluginInstance; + if (typeof(Plugin.activate) !== 'undefined') { + pluginInstance = Plugin; + } else { + pluginInstance = new Plugin(); + } + + var pluginDict = { + plugin: pluginInstance, + events: {}, + options: {}, + pluginOptions: {} + }; + + var handlers = pluginInstance.activate(this); + for (var eventName in handlers) { + if (!handlers.hasOwnProperty(eventName)) continue; + // TODO(danvk): validate eventName. + pluginDict.events[eventName] = handlers[eventName]; + } + + this.plugins_.push(pluginDict); + } + + // At this point, plugins can no longer register event handlers. + // Construct a map from event -> ordered list of [callback, plugin]. + for (var i = 0; i < this.plugins_.length; i++) { + var plugin_dict = this.plugins_[i]; + for (var eventName in plugin_dict.events) { + if (!plugin_dict.events.hasOwnProperty(eventName)) continue; + var callback = plugin_dict.events[eventName]; + + var pair = [plugin_dict.plugin, callback]; + if (!(eventName in this.eventListeners_)) { + this.eventListeners_[eventName] = [pair]; + } else { + this.eventListeners_[eventName].push(pair); + } + } + } + + this.createDragInterface_(); + + this.start_(); +}; + +/** + * Triggers a cascade of events to the various plugins which are interested in them. + * Returns true if the "default behavior" should be prevented, i.e. if one + * of the event listeners called event.preventDefault(). + * @private + */ +Dygraph.prototype.cascadeEvents_ = function(name, extra_props) { + if (!(name in this.eventListeners_)) return false; + + // QUESTION: can we use objects & prototypes to speed this up? + var e = { + dygraph: this, + cancelable: false, + defaultPrevented: false, + preventDefault: function() { + if (!e.cancelable) throw "Cannot call preventDefault on non-cancelable event."; + e.defaultPrevented = true; + }, + propagationStopped: false, + stopPropagation: function() { + e.propagationStopped = true; + } + }; + utils.update(e, extra_props); + + var callback_plugin_pairs = this.eventListeners_[name]; + if (callback_plugin_pairs) { + for (var i = callback_plugin_pairs.length - 1; i >= 0; i--) { + var plugin = callback_plugin_pairs[i][0]; + var callback = callback_plugin_pairs[i][1]; + callback.call(plugin, e); + if (e.propagationStopped) break; + } + } + return e.defaultPrevented; +}; + +/** + * Fetch a plugin instance of a particular class. Only for testing. + * @private + * @param {!Class} type The type of the plugin. + * @return {Object} Instance of the plugin, or null if there is none. + */ +Dygraph.prototype.getPluginInstance_ = function(type) { + for (var i = 0; i < this.plugins_.length; i++) { + var p = this.plugins_[i]; + if (p.plugin instanceof type) { + return p.plugin; + } + } + return null; +}; + +/** + * Returns the zoomed status of the chart for one or both axes. + * + * Axis is an optional parameter. Can be set to 'x' or 'y'. + * + * The zoomed status for an axis is set whenever a user zooms using the mouse + * or when the dateWindow or valueRange are updated. Double-clicking or calling + * resetZoom() resets the zoom status for the chart. + */ +Dygraph.prototype.isZoomed = function(axis) { + const isZoomedX = !!this.dateWindow_; + if (axis === 'x') return isZoomedX; + + const isZoomedY = this.axes_.map(axis => !!axis.valueRange).indexOf(true) >= 0; + if (axis === null || axis === undefined) { + return isZoomedX || isZoomedY; + } + if (axis === 'y') return isZoomedY; + + throw new Error(`axis parameter is [${axis}] must be null, 'x' or 'y'.`); +}; + +/** + * Returns information about the Dygraph object, including its containing ID. + */ +Dygraph.prototype.toString = function() { + var maindiv = this.maindiv_; + var id = (maindiv && maindiv.id) ? maindiv.id : maindiv; + return "[Dygraph " + id + "]"; +}; + +/** + * @private + * Returns the value of an option. This may be set by the user (either in the + * constructor or by calling updateOptions) or by dygraphs, and may be set to a + * per-series value. + * @param {string} name The name of the option, e.g. 'rollPeriod'. + * @param {string} [seriesName] The name of the series to which the option + * will be applied. If no per-series value of this option is available, then + * the global value is returned. This is optional. + * @return { ... } The value of the option. + */ +Dygraph.prototype.attr_ = function(name, seriesName) { + // For "production" code, this gets removed by uglifyjs. + if (typeof(process) !== 'undefined') { + if (process.env.NODE_ENV != 'production') { + if (typeof(OPTIONS_REFERENCE) === 'undefined') { + console.error('Must include options reference JS for testing'); + } else if (!OPTIONS_REFERENCE.hasOwnProperty(name)) { + console.error('Dygraphs is using property ' + name + ', which has no ' + + 'entry in the Dygraphs.OPTIONS_REFERENCE listing.'); + // Only log this error once. + OPTIONS_REFERENCE[name] = true; + } + } + } + return seriesName ? this.attributes_.getForSeries(name, seriesName) : this.attributes_.get(name); +}; + +/** + * Returns the current value for an option, as set in the constructor or via + * updateOptions. You may pass in an (optional) series name to get per-series + * values for the option. + * + * All values returned by this method should be considered immutable. If you + * modify them, there is no guarantee that the changes will be honored or that + * dygraphs will remain in a consistent state. If you want to modify an option, + * use updateOptions() instead. + * + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {*} The value of the option. + */ +Dygraph.prototype.getOption = function(name, opt_seriesName) { + return this.attr_(name, opt_seriesName); +}; + +/** + * Like getOption(), but specifically returns a number. + * This is a convenience function for working with the Closure Compiler. + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {number} The value of the option. + * @private + */ +Dygraph.prototype.getNumericOption = function(name, opt_seriesName) { + return /** @type{number} */(this.getOption(name, opt_seriesName)); +}; + +/** + * Like getOption(), but specifically returns a string. + * This is a convenience function for working with the Closure Compiler. + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {string} The value of the option. + * @private + */ +Dygraph.prototype.getStringOption = function(name, opt_seriesName) { + return /** @type{string} */(this.getOption(name, opt_seriesName)); +}; + +/** + * Like getOption(), but specifically returns a boolean. + * This is a convenience function for working with the Closure Compiler. + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {boolean} The value of the option. + * @private + */ +Dygraph.prototype.getBooleanOption = function(name, opt_seriesName) { + return /** @type{boolean} */(this.getOption(name, opt_seriesName)); +}; + +/** + * Like getOption(), but specifically returns a function. + * This is a convenience function for working with the Closure Compiler. + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {function(...)} The value of the option. + * @private + */ +Dygraph.prototype.getFunctionOption = function(name, opt_seriesName) { + return /** @type{function(...)} */(this.getOption(name, opt_seriesName)); +}; + +Dygraph.prototype.getOptionForAxis = function(name, axis) { + return this.attributes_.getForAxis(name, axis); +}; + +/** + * @private + * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2') + * @return { ... } A function mapping string -> option value + */ +Dygraph.prototype.optionsViewForAxis_ = function(axis) { + var self = this; + return function(opt) { + var axis_opts = self.user_attrs_.axes; + if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) { + return axis_opts[axis][opt]; + } + + // I don't like that this is in a second spot. + if (axis === 'x' && opt === 'logscale') { + // return the default value. + // TODO(konigsberg): pull the default from a global default. + return false; + } + + // user-specified attributes always trump defaults, even if they're less + // specific. + if (typeof(self.user_attrs_[opt]) != 'undefined') { + return self.user_attrs_[opt]; + } + + axis_opts = self.attrs_.axes; + if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) { + return axis_opts[axis][opt]; + } + // check old-style axis options + // TODO(danvk): add a deprecation warning if either of these match. + if (axis == 'y' && self.axes_[0].hasOwnProperty(opt)) { + return self.axes_[0][opt]; + } else if (axis == 'y2' && self.axes_[1].hasOwnProperty(opt)) { + return self.axes_[1][opt]; + } + return self.attr_(opt); + }; +}; + +/** + * Returns the current rolling period, as set by the user or an option. + * @return {number} The number of points in the rolling window + */ +Dygraph.prototype.rollPeriod = function() { + return this.rollPeriod_; +}; + +/** + * Returns the currently-visible x-range. This can be affected by zooming, + * panning or a call to updateOptions. + * Returns a two-element array: [left, right]. + * If the Dygraph has dates on the x-axis, these will be millis since epoch. + */ +Dygraph.prototype.xAxisRange = function() { + return this.dateWindow_ ? this.dateWindow_ : this.xAxisExtremes(); +}; + +/** + * Returns the lower- and upper-bound x-axis values of the data set. + */ +Dygraph.prototype.xAxisExtremes = function() { + var pad = this.getNumericOption('xRangePad') / this.plotter_.area.w; + if (this.numRows() === 0) { + return [0 - pad, 1 + pad]; + } + var left = this.rawData_[0][0]; + var right = this.rawData_[this.rawData_.length - 1][0]; + if (pad) { + // Must keep this in sync with dygraph-layout _evaluateLimits() + var range = right - left; + left -= range * pad; + right += range * pad; + } + return [left, right]; +}; + +/** + * Returns the lower- and upper-bound y-axis values for each axis. These are + * the ranges you'll get if you double-click to zoom out or call resetZoom(). + * The return value is an array of [low, high] tuples, one for each y-axis. + */ +Dygraph.prototype.yAxisExtremes = function() { + // TODO(danvk): this is pretty inefficient + const packed = this.gatherDatasets_(this.rolledSeries_, null); + const { extremes } = packed; + const saveAxes = this.axes_; + this.computeYAxisRanges_(extremes); + const newAxes = this.axes_; + this.axes_ = saveAxes; + return newAxes.map(axis => axis.extremeRange); +} + +/** + * Returns the currently-visible y-range for an axis. This can be affected by + * zooming, panning or a call to updateOptions. Axis indices are zero-based. If + * called with no arguments, returns the range of the first axis. + * Returns a two-element array: [bottom, top]. + */ +Dygraph.prototype.yAxisRange = function(idx) { + if (typeof(idx) == "undefined") idx = 0; + if (idx < 0 || idx >= this.axes_.length) { + return null; + } + var axis = this.axes_[idx]; + return [ axis.computedValueRange[0], axis.computedValueRange[1] ]; +}; + +/** + * Returns the currently-visible y-ranges for each axis. This can be affected by + * zooming, panning, calls to updateOptions, etc. + * Returns an array of [bottom, top] pairs, one for each y-axis. + */ +Dygraph.prototype.yAxisRanges = function() { + var ret = []; + for (var i = 0; i < this.axes_.length; i++) { + ret.push(this.yAxisRange(i)); + } + return ret; +}; + +// TODO(danvk): use these functions throughout dygraphs. +/** + * Convert from data coordinates to canvas/div X/Y coordinates. + * If specified, do this conversion for the coordinate system of a particular + * axis. Uses the first axis by default. + * Returns a two-element array: [X, Y] + * + * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord + * instead of toDomCoords(null, y, axis). + */ +Dygraph.prototype.toDomCoords = function(x, y, axis) { + return [ this.toDomXCoord(x), this.toDomYCoord(y, axis) ]; +}; + +/** + * Convert from data x coordinates to canvas/div X coordinate. + * If specified, do this conversion for the coordinate system of a particular + * axis. + * Returns a single value or null if x is null. + */ +Dygraph.prototype.toDomXCoord = function(x) { + if (x === null) { + return null; + } + + var area = this.plotter_.area; + var xRange = this.xAxisRange(); + return area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w; +}; + +/** + * Convert from data x coordinates to canvas/div Y coordinate and optional + * axis. Uses the first axis by default. + * + * returns a single value or null if y is null. + */ +Dygraph.prototype.toDomYCoord = function(y, axis) { + var pct = this.toPercentYCoord(y, axis); + + if (pct === null) { + return null; + } + var area = this.plotter_.area; + return area.y + pct * area.h; +}; + +/** + * Convert from canvas/div coords to data coordinates. + * If specified, do this conversion for the coordinate system of a particular + * axis. Uses the first axis by default. + * Returns a two-element array: [X, Y]. + * + * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord + * instead of toDataCoords(null, y, axis). + */ +Dygraph.prototype.toDataCoords = function(x, y, axis) { + return [ this.toDataXCoord(x), this.toDataYCoord(y, axis) ]; +}; + +/** + * Convert from canvas/div x coordinate to data coordinate. + * + * If x is null, this returns null. + */ +Dygraph.prototype.toDataXCoord = function(x) { + if (x === null) { + return null; + } + + var area = this.plotter_.area; + var xRange = this.xAxisRange(); + + if (!this.attributes_.getForAxis("logscale", 'x')) { + return xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]); + } else { + var pct = (x - area.x) / area.w; + return utils.logRangeFraction(xRange[0], xRange[1], pct); + } +}; + +/** + * Convert from canvas/div y coord to value. + * + * If y is null, this returns null. + * if axis is null, this uses the first axis. + */ +Dygraph.prototype.toDataYCoord = function(y, axis) { + if (y === null) { + return null; + } + + var area = this.plotter_.area; + var yRange = this.yAxisRange(axis); + + if (typeof(axis) == "undefined") axis = 0; + if (!this.attributes_.getForAxis("logscale", axis)) { + return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]); + } else { + // Computing the inverse of toDomCoord. + var pct = (y - area.y) / area.h; + // Note reversed yRange, y1 is on top with pct==0. + return utils.logRangeFraction(yRange[1], yRange[0], pct); + } +}; + +/** + * Converts a y for an axis to a percentage from the top to the + * bottom of the drawing area. + * + * If the coordinate represents a value visible on the canvas, then + * the value will be between 0 and 1, where 0 is the top of the canvas. + * However, this method will return values outside the range, as + * values can fall outside the canvas. + * + * If y is null, this returns null. + * if axis is null, this uses the first axis. + * + * @param {number} y The data y-coordinate. + * @param {number} [axis] The axis number on which the data coordinate lives. + * @return {number} A fraction in [0, 1] where 0 = the top edge. + */ +Dygraph.prototype.toPercentYCoord = function(y, axis) { + if (y === null) { + return null; + } + if (typeof(axis) == "undefined") axis = 0; + + var yRange = this.yAxisRange(axis); + + var pct; + var logscale = this.attributes_.getForAxis("logscale", axis); + if (logscale) { + var logr0 = utils.log10(yRange[0]); + var logr1 = utils.log10(yRange[1]); + pct = (logr1 - utils.log10(y)) / (logr1 - logr0); + } else { + // yRange[1] - y is unit distance from the bottom. + // yRange[1] - yRange[0] is the scale of the range. + // (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom. + pct = (yRange[1] - y) / (yRange[1] - yRange[0]); + } + return pct; +}; + +/** + * Converts an x value to a percentage from the left to the right of + * the drawing area. + * + * If the coordinate represents a value visible on the canvas, then + * the value will be between 0 and 1, where 0 is the left of the canvas. + * However, this method will return values outside the range, as + * values can fall outside the canvas. + * + * If x is null, this returns null. + * @param {number} x The data x-coordinate. + * @return {number} A fraction in [0, 1] where 0 = the left edge. + */ +Dygraph.prototype.toPercentXCoord = function(x) { + if (x === null) { + return null; + } + + var xRange = this.xAxisRange(); + var pct; + var logscale = this.attributes_.getForAxis("logscale", 'x') ; + if (logscale === true) { // logscale can be null so we test for true explicitly. + var logr0 = utils.log10(xRange[0]); + var logr1 = utils.log10(xRange[1]); + pct = (utils.log10(x) - logr0) / (logr1 - logr0); + } else { + // x - xRange[0] is unit distance from the left. + // xRange[1] - xRange[0] is the scale of the range. + // The full expression below is the % from the left. + pct = (x - xRange[0]) / (xRange[1] - xRange[0]); + } + return pct; +}; + +/** + * Returns the number of columns (including the independent variable). + * @return {number} The number of columns. + */ +Dygraph.prototype.numColumns = function() { + if (!this.rawData_) return 0; + return this.rawData_[0] ? this.rawData_[0].length : this.attr_("labels").length; +}; + +/** + * Returns the number of rows (excluding any header/label row). + * @return {number} The number of rows, less any header. + */ +Dygraph.prototype.numRows = function() { + if (!this.rawData_) return 0; + return this.rawData_.length; +}; + +/** + * Returns the value in the given row and column. If the row and column exceed + * the bounds on the data, returns null. Also returns null if the value is + * missing. + * @param {number} row The row number of the data (0-based). Row 0 is the + * first row of data, not a header row. + * @param {number} col The column number of the data (0-based) + * @return {number} The value in the specified cell or null if the row/col + * were out of range. + */ +Dygraph.prototype.getValue = function(row, col) { + if (row < 0 || row > this.rawData_.length) return null; + if (col < 0 || col > this.rawData_[row].length) return null; + + return this.rawData_[row][col]; +}; + +/** + * Generates interface elements for the Dygraph: a containing div, a div to + * display the current point, and a textbox to adjust the rolling average + * period. Also creates the Renderer/Layout elements. + * @private + */ +Dygraph.prototype.createInterface_ = function() { + // Create the all-enclosing graph div + var enclosing = this.maindiv_; + + this.graphDiv = document.createElement("div"); + + // TODO(danvk): any other styles that are useful to set here? + this.graphDiv.style.textAlign = 'left'; // This is a CSS "reset" + this.graphDiv.style.position = 'relative'; + enclosing.appendChild(this.graphDiv); + + // Create the canvas for interactive parts of the chart. + this.canvas_ = utils.createCanvas(); + this.canvas_.style.position = "absolute"; + + // ... and for static parts of the chart. + this.hidden_ = this.createPlotKitCanvas_(this.canvas_); + + this.canvas_ctx_ = utils.getContext(this.canvas_); + this.hidden_ctx_ = utils.getContext(this.hidden_); + + this.resizeElements_(); + + // The interactive parts of the graph are drawn on top of the chart. + this.graphDiv.appendChild(this.hidden_); + this.graphDiv.appendChild(this.canvas_); + this.mouseEventElement_ = this.createMouseEventElement_(); + + // Create the grapher + this.layout_ = new DygraphLayout(this); + + var dygraph = this; + + this.mouseMoveHandler_ = function(e) { + dygraph.mouseMove_(e); + }; + + this.mouseOutHandler_ = function(e) { + // The mouse has left the chart if: + // 1. e.target is inside the chart + // 2. e.relatedTarget is outside the chart + var target = e.target || e.fromElement; + var relatedTarget = e.relatedTarget || e.toElement; + if (utils.isNodeContainedBy(target, dygraph.graphDiv) && + !utils.isNodeContainedBy(relatedTarget, dygraph.graphDiv)) { + dygraph.mouseOut_(e); + } + }; + + this.addAndTrackEvent(window, 'mouseout', this.mouseOutHandler_); + this.addAndTrackEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_); + + // Don't recreate and register the resize handler on subsequent calls. + // This happens when the graph is resized. + if (!this.resizeHandler_) { + this.resizeHandler_ = function(e) { + dygraph.resize(); + }; + + // Update when the window is resized. + // TODO(danvk): drop frames depending on complexity of the chart. + this.addAndTrackEvent(window, 'resize', this.resizeHandler_); + } +}; + +Dygraph.prototype.resizeElements_ = function() { + this.graphDiv.style.width = this.width_ + "px"; + this.graphDiv.style.height = this.height_ + "px"; + + var pixelRatioOption = this.getNumericOption('pixelRatio') + + var canvasScale = pixelRatioOption || utils.getContextPixelRatio(this.canvas_ctx_); + this.canvas_.width = this.width_ * canvasScale; + this.canvas_.height = this.height_ * canvasScale; + this.canvas_.style.width = this.width_ + "px"; // for IE + this.canvas_.style.height = this.height_ + "px"; // for IE + if (canvasScale !== 1) { + this.canvas_ctx_.scale(canvasScale, canvasScale); + } + + var hiddenScale = pixelRatioOption || utils.getContextPixelRatio(this.hidden_ctx_); + this.hidden_.width = this.width_ * hiddenScale; + this.hidden_.height = this.height_ * hiddenScale; + this.hidden_.style.width = this.width_ + "px"; // for IE + this.hidden_.style.height = this.height_ + "px"; // for IE + if (hiddenScale !== 1) { + this.hidden_ctx_.scale(hiddenScale, hiddenScale); + } +}; + +/** + * Detach DOM elements in the dygraph and null out all data references. + * Calling this when you're done with a dygraph can dramatically reduce memory + * usage. See, e.g., the tests/perf.html example. + */ +Dygraph.prototype.destroy = function() { + this.canvas_ctx_.restore(); + this.hidden_ctx_.restore(); + + // Destroy any plugins, in the reverse order that they were registered. + for (var i = this.plugins_.length - 1; i >= 0; i--) { + var p = this.plugins_.pop(); + if (p.plugin.destroy) p.plugin.destroy(); + } + + var removeRecursive = function(node) { + while (node.hasChildNodes()) { + removeRecursive(node.firstChild); + node.removeChild(node.firstChild); + } + }; + + this.removeTrackedEvents_(); + + // remove mouse event handlers (This may not be necessary anymore) + utils.removeEvent(window, 'mouseout', this.mouseOutHandler_); + utils.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_); + + // remove window handlers + utils.removeEvent(window,'resize', this.resizeHandler_); + this.resizeHandler_ = null; + + removeRecursive(this.maindiv_); + + var nullOut = function(obj) { + for (var n in obj) { + if (typeof(obj[n]) === 'object') { + obj[n] = null; + } + } + }; + // These may not all be necessary, but it can't hurt... + nullOut(this.layout_); + nullOut(this.plotter_); + nullOut(this); +}; + +/** + * Creates the canvas on which the chart will be drawn. Only the Renderer ever + * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots + * or the zoom rectangles) is done on this.canvas_. + * @param {Object} canvas The Dygraph canvas over which to overlay the plot + * @return {Object} The newly-created canvas + * @private + */ +Dygraph.prototype.createPlotKitCanvas_ = function(canvas) { + var h = utils.createCanvas(); + h.style.position = "absolute"; + // TODO(danvk): h should be offset from canvas. canvas needs to include + // some extra area to make it easier to zoom in on the far left and far + // right. h needs to be precisely the plot area, so that clipping occurs. + h.style.top = canvas.style.top; + h.style.left = canvas.style.left; + h.width = this.width_; + h.height = this.height_; + h.style.width = this.width_ + "px"; // for IE + h.style.height = this.height_ + "px"; // for IE + return h; +}; + +/** + * Creates an overlay element used to handle mouse events. + * @return {Object} The mouse event element. + * @private + */ +Dygraph.prototype.createMouseEventElement_ = function() { + return this.canvas_; +}; + +/** + * Generate a set of distinct colors for the data series. This is done with a + * color wheel. Saturation/Value are customizable, and the hue is + * equally-spaced around the color wheel. If a custom set of colors is + * specified, that is used instead. + * @private + */ +Dygraph.prototype.setColors_ = function() { + var labels = this.getLabels(); + var num = labels.length - 1; + this.colors_ = []; + this.colorsMap_ = {}; + + // These are used for when no custom colors are specified. + var sat = this.getNumericOption('colorSaturation') || 1.0; + var val = this.getNumericOption('colorValue') || 0.5; + var half = Math.ceil(num / 2); + + var colors = this.getOption('colors'); + var visibility = this.visibility(); + for (var i = 0; i < num; i++) { + if (!visibility[i]) { + continue; + } + var label = labels[i + 1]; + var colorStr = this.attributes_.getForSeries('color', label); + if (!colorStr) { + if (colors) { + colorStr = colors[i % colors.length]; + } else { + // alternate colors for high contrast. + var idx = i % 2 ? (half + (i + 1)/ 2) : Math.ceil((i + 1) / 2); + var hue = (1.0 * idx / (1 + num)); + colorStr = utils.hsvToRGB(hue, sat, val); + } + } + this.colors_.push(colorStr); + this.colorsMap_[label] = colorStr; + } +}; + +/** + * Return the list of colors. This is either the list of colors passed in the + * attributes or the autogenerated list of rgb(r,g,b) strings. + * This does not return colors for invisible series. + * @return {Array.} The list of colors. + */ +Dygraph.prototype.getColors = function() { + return this.colors_; +}; + +/** + * Returns a few attributes of a series, i.e. its color, its visibility, which + * axis it's assigned to, and its column in the original data. + * Returns null if the series does not exist. + * Otherwise, returns an object with column, visibility, color and axis properties. + * The "axis" property will be set to 1 for y1 and 2 for y2. + * The "column" property can be fed back into getValue(row, column) to get + * values for this series. + */ +Dygraph.prototype.getPropertiesForSeries = function(series_name) { + var idx = -1; + var labels = this.getLabels(); + for (var i = 1; i < labels.length; i++) { + if (labels[i] == series_name) { + idx = i; + break; + } + } + if (idx == -1) return null; + + return { + name: series_name, + column: idx, + visible: this.visibility()[idx - 1], + color: this.colorsMap_[series_name], + axis: 1 + this.attributes_.axisForSeries(series_name) + }; +}; + +/** + * Create the text box to adjust the averaging period + * @private + */ +Dygraph.prototype.createRollInterface_ = function() { + // Create a roller if one doesn't exist already. + var roller = this.roller_; + if (!roller) { + this.roller_ = roller = document.createElement("input"); + roller.type = "text"; + roller.style.display = "none"; + roller.className = 'dygraph-roller'; + this.graphDiv.appendChild(roller); + } + + var display = this.getBooleanOption('showRoller') ? 'block' : 'none'; + + var area = this.getArea(); + var textAttr = { + "top": (area.y + area.h - 25) + "px", + "left": (area.x + 1) + "px", + "display": display + }; + roller.size = "2"; + roller.value = this.rollPeriod_; + utils.update(roller.style, textAttr); + + roller.onchange = () => this.adjustRoll(roller.value); +}; + +/** + * Set up all the mouse handlers needed to capture dragging behavior for zoom + * events. + * @private + */ +Dygraph.prototype.createDragInterface_ = function() { + var context = { + // Tracks whether the mouse is down right now + isZooming: false, + isPanning: false, // is this drag part of a pan? + is2DPan: false, // if so, is that pan 1- or 2-dimensional? + dragStartX: null, // pixel coordinates + dragStartY: null, // pixel coordinates + dragEndX: null, // pixel coordinates + dragEndY: null, // pixel coordinates + dragDirection: null, + prevEndX: null, // pixel coordinates + prevEndY: null, // pixel coordinates + prevDragDirection: null, + cancelNextDblclick: false, // see comment in dygraph-interaction-model.js + + // The value on the left side of the graph when a pan operation starts. + initialLeftmostDate: null, + + // The number of units each pixel spans. (This won't be valid for log + // scales) + xUnitsPerPixel: null, + + // TODO(danvk): update this comment + // The range in second/value units that the viewport encompasses during a + // panning operation. + dateRange: null, + + // Top-left corner of the canvas, in DOM coords + // TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY. + px: 0, + py: 0, + + // Values for use with panEdgeFraction, which limit how far outside the + // graph's data boundaries it can be panned. + boundedDates: null, // [minDate, maxDate] + boundedValues: null, // [[minValue, maxValue] ...] + + // We cover iframes during mouse interactions. See comments in + // dygraph-utils.js for more info on why this is a good idea. + tarp: new IFrameTarp(), + + // contextB is the same thing as this context object but renamed. + initializeMouseDown: function(event, g, contextB) { + // prevents mouse drags from selecting page text. + if (event.preventDefault) { + event.preventDefault(); // Firefox, Chrome, etc. + } else { + event.returnValue = false; // IE + event.cancelBubble = true; + } + + var canvasPos = utils.findPos(g.canvas_); + contextB.px = canvasPos.x; + contextB.py = canvasPos.y; + contextB.dragStartX = utils.dragGetX_(event, contextB); + contextB.dragStartY = utils.dragGetY_(event, contextB); + contextB.cancelNextDblclick = false; + contextB.tarp.cover(); + }, + destroy: function() { + var context = this; + if (context.isZooming || context.isPanning) { + context.isZooming = false; + context.dragStartX = null; + context.dragStartY = null; + } + + if (context.isPanning) { + context.isPanning = false; + context.draggingDate = null; + context.dateRange = null; + for (var i = 0; i < self.axes_.length; i++) { + delete self.axes_[i].draggingValue; + delete self.axes_[i].dragValueRange; + } + } + + context.tarp.uncover(); + } + }; + + var interactionModel = this.getOption("interactionModel"); + + // Self is the graph. + var self = this; + + // Function that binds the graph and context to the handler. + var bindHandler = function(handler) { + return function(event) { + handler(event, self, context); + }; + }; + + for (var eventName in interactionModel) { + if (!interactionModel.hasOwnProperty(eventName)) continue; + this.addAndTrackEvent(this.mouseEventElement_, eventName, + bindHandler(interactionModel[eventName])); + } + + // If the user releases the mouse button during a drag, but not over the + // canvas, then it doesn't count as a zooming action. + if (!interactionModel.willDestroyContextMyself) { + var mouseUpHandler = function(event) { + context.destroy(); + }; + + this.addAndTrackEvent(document, 'mouseup', mouseUpHandler); + } +}; + +/** + * Draw a gray zoom rectangle over the desired area of the canvas. Also clears + * up any previous zoom rectangles that were drawn. This could be optimized to + * avoid extra redrawing, but it's tricky to avoid interactions with the status + * dots. + * + * @param {number} direction the direction of the zoom rectangle. Acceptable + * values are utils.HORIZONTAL and utils.VERTICAL. + * @param {number} startX The X position where the drag started, in canvas + * coordinates. + * @param {number} endX The current X position of the drag, in canvas coords. + * @param {number} startY The Y position where the drag started, in canvas + * coordinates. + * @param {number} endY The current Y position of the drag, in canvas coords. + * @param {number} prevDirection the value of direction on the previous call to + * this function. Used to avoid excess redrawing + * @param {number} prevEndX The value of endX on the previous call to this + * function. Used to avoid excess redrawing + * @param {number} prevEndY The value of endY on the previous call to this + * function. Used to avoid excess redrawing + * @private + */ +Dygraph.prototype.drawZoomRect_ = function(direction, startX, endX, startY, + endY, prevDirection, prevEndX, + prevEndY) { + var ctx = this.canvas_ctx_; + + // Clean up from the previous rect if necessary + if (prevDirection == utils.HORIZONTAL) { + ctx.clearRect(Math.min(startX, prevEndX), this.layout_.getPlotArea().y, + Math.abs(startX - prevEndX), this.layout_.getPlotArea().h); + } else if (prevDirection == utils.VERTICAL) { + ctx.clearRect(this.layout_.getPlotArea().x, Math.min(startY, prevEndY), + this.layout_.getPlotArea().w, Math.abs(startY - prevEndY)); + } + + // Draw a light-grey rectangle to show the new viewing area + if (direction == utils.HORIZONTAL) { + if (endX && startX) { + ctx.fillStyle = "rgba(128,128,128,0.33)"; + ctx.fillRect(Math.min(startX, endX), this.layout_.getPlotArea().y, + Math.abs(endX - startX), this.layout_.getPlotArea().h); + } + } else if (direction == utils.VERTICAL) { + if (endY && startY) { + ctx.fillStyle = "rgba(128,128,128,0.33)"; + ctx.fillRect(this.layout_.getPlotArea().x, Math.min(startY, endY), + this.layout_.getPlotArea().w, Math.abs(endY - startY)); + } + } +}; + +/** + * Clear the zoom rectangle (and perform no zoom). + * @private + */ +Dygraph.prototype.clearZoomRect_ = function() { + this.currentZoomRectArgs_ = null; + this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_); +}; + +/** + * Zoom to something containing [lowX, highX]. These are pixel coordinates in + * the canvas. The exact zoom window may be slightly larger if there are no data + * points near lowX or highX. Don't confuse this function with doZoomXDates, + * which accepts dates that match the raw data. This function redraws the graph. + * + * @param {number} lowX The leftmost pixel value that should be visible. + * @param {number} highX The rightmost pixel value that should be visible. + * @private + */ +Dygraph.prototype.doZoomX_ = function(lowX, highX) { + this.currentZoomRectArgs_ = null; + // Find the earliest and latest dates contained in this canvasx range. + // Convert the call to date ranges of the raw data. + var minDate = this.toDataXCoord(lowX); + var maxDate = this.toDataXCoord(highX); + this.doZoomXDates_(minDate, maxDate); +}; + +/** + * Zoom to something containing [minDate, maxDate] values. Don't confuse this + * method with doZoomX which accepts pixel coordinates. This function redraws + * the graph. + * + * @param {number} minDate The minimum date that should be visible. + * @param {number} maxDate The maximum date that should be visible. + * @private + */ +Dygraph.prototype.doZoomXDates_ = function(minDate, maxDate) { + // TODO(danvk): when xAxisRange is null (i.e. "fit to data", the animation + // can produce strange effects. Rather than the x-axis transitioning slowly + // between values, it can jerk around.) + var old_window = this.xAxisRange(); + var new_window = [minDate, maxDate]; + const zoomCallback = this.getFunctionOption('zoomCallback'); + this.doAnimatedZoom(old_window, new_window, null, null, () => { + if (zoomCallback) { + zoomCallback.call(this, minDate, maxDate, this.yAxisRanges()); + } + }); +}; + +/** + * Zoom to something containing [lowY, highY]. These are pixel coordinates in + * the canvas. This function redraws the graph. + * + * @param {number} lowY The topmost pixel value that should be visible. + * @param {number} highY The lowest pixel value that should be visible. + * @private + */ +Dygraph.prototype.doZoomY_ = function(lowY, highY) { + this.currentZoomRectArgs_ = null; + // Find the highest and lowest values in pixel range for each axis. + // Note that lowY (in pixels) corresponds to the max Value (in data coords). + // This is because pixels increase as you go down on the screen, whereas data + // coordinates increase as you go up the screen. + var oldValueRanges = this.yAxisRanges(); + var newValueRanges = []; + for (var i = 0; i < this.axes_.length; i++) { + var hi = this.toDataYCoord(lowY, i); + var low = this.toDataYCoord(highY, i); + newValueRanges.push([low, hi]); + } + + const zoomCallback = this.getFunctionOption('zoomCallback'); + this.doAnimatedZoom(null, null, oldValueRanges, newValueRanges, () => { + if (zoomCallback) { + const [minX, maxX] = this.xAxisRange(); + zoomCallback.call(this, minX, maxX, this.yAxisRanges()); + } + }); +}; + +/** + * Transition function to use in animations. Returns values between 0.0 + * (totally old values) and 1.0 (totally new values) for each frame. + * @private + */ +Dygraph.zoomAnimationFunction = function(frame, numFrames) { + var k = 1.5; + return (1.0 - Math.pow(k, -frame)) / (1.0 - Math.pow(k, -numFrames)); +}; + +/** + * Reset the zoom to the original view coordinates. This is the same as + * double-clicking on the graph. + */ +Dygraph.prototype.resetZoom = function() { + const dirtyX = this.isZoomed('x'); + const dirtyY = this.isZoomed('y'); + const dirty = dirtyX || dirtyY; + + // Clear any selection, since it's likely to be drawn in the wrong place. + this.clearSelection(); + + if (!dirty) return; + + // Calculate extremes to avoid lack of padding on reset. + const [minDate, maxDate] = this.xAxisExtremes(); + + const animatedZooms = this.getBooleanOption('animatedZooms'); + const zoomCallback = this.getFunctionOption('zoomCallback'); + + // TODO(danvk): merge this block w/ the code below. + // TODO(danvk): factor out a generic, public zoomTo method. + if (!animatedZooms) { + this.dateWindow_ = null; + this.axes_.forEach(axis => { + if (axis.valueRange) delete axis.valueRange; + }); + + this.drawGraph_(); + if (zoomCallback) { + zoomCallback.call(this, minDate, maxDate, this.yAxisRanges()); + } + return; + } + + var oldWindow=null, newWindow=null, oldValueRanges=null, newValueRanges=null; + if (dirtyX) { + oldWindow = this.xAxisRange(); + newWindow = [minDate, maxDate]; + } + + if (dirtyY) { + oldValueRanges = this.yAxisRanges(); + newValueRanges = this.yAxisExtremes(); + } + + this.doAnimatedZoom(oldWindow, newWindow, oldValueRanges, newValueRanges, + () => { + this.dateWindow_ = null; + this.axes_.forEach(axis => { + if (axis.valueRange) delete axis.valueRange; + }); + if (zoomCallback) { + zoomCallback.call(this, minDate, maxDate, this.yAxisRanges()); + } + }); +}; + +/** + * Combined animation logic for all zoom functions. + * either the x parameters or y parameters may be null. + * @private + */ +Dygraph.prototype.doAnimatedZoom = function(oldXRange, newXRange, oldYRanges, newYRanges, callback) { + var steps = this.getBooleanOption("animatedZooms") ? + Dygraph.ANIMATION_STEPS : 1; + + var windows = []; + var valueRanges = []; + var step, frac; + + if (oldXRange !== null && newXRange !== null) { + for (step = 1; step <= steps; step++) { + frac = Dygraph.zoomAnimationFunction(step, steps); + windows[step-1] = [oldXRange[0]*(1-frac) + frac*newXRange[0], + oldXRange[1]*(1-frac) + frac*newXRange[1]]; + } + } + + if (oldYRanges !== null && newYRanges !== null) { + for (step = 1; step <= steps; step++) { + frac = Dygraph.zoomAnimationFunction(step, steps); + var thisRange = []; + for (var j = 0; j < this.axes_.length; j++) { + thisRange.push([oldYRanges[j][0]*(1-frac) + frac*newYRanges[j][0], + oldYRanges[j][1]*(1-frac) + frac*newYRanges[j][1]]); + } + valueRanges[step-1] = thisRange; + } + } + + utils.repeatAndCleanup(step => { + if (valueRanges.length) { + for (var i = 0; i < this.axes_.length; i++) { + var w = valueRanges[step][i]; + this.axes_[i].valueRange = [w[0], w[1]]; + } + } + if (windows.length) { + this.dateWindow_ = windows[step]; + } + this.drawGraph_(); + }, steps, Dygraph.ANIMATION_DURATION / steps, callback); +}; + +/** + * Get the current graph's area object. + * + * Returns: {x, y, w, h} + */ +Dygraph.prototype.getArea = function() { + return this.plotter_.area; +}; + +/** + * Convert a mouse event to DOM coordinates relative to the graph origin. + * + * Returns a two-element array: [X, Y]. + */ +Dygraph.prototype.eventToDomCoords = function(event) { + if (event.offsetX && event.offsetY) { + return [ event.offsetX, event.offsetY ]; + } else { + var eventElementPos = utils.findPos(this.mouseEventElement_); + var canvasx = utils.pageX(event) - eventElementPos.x; + var canvasy = utils.pageY(event) - eventElementPos.y; + return [canvasx, canvasy]; + } +}; + +/** + * Given a canvas X coordinate, find the closest row. + * @param {number} domX graph-relative DOM X coordinate + * Returns {number} row number. + * @private + */ +Dygraph.prototype.findClosestRow = function(domX) { + var minDistX = Infinity; + var closestRow = -1; + var sets = this.layout_.points; + for (var i = 0; i < sets.length; i++) { + var points = sets[i]; + var len = points.length; + for (var j = 0; j < len; j++) { + var point = points[j]; + if (!utils.isValidPoint(point, true)) continue; + var dist = Math.abs(point.canvasx - domX); + if (dist < minDistX) { + minDistX = dist; + closestRow = point.idx; + } + } + } + + return closestRow; +}; + +/** + * Given canvas X,Y coordinates, find the closest point. + * + * This finds the individual data point across all visible series + * that's closest to the supplied DOM coordinates using the standard + * Euclidean X,Y distance. + * + * @param {number} domX graph-relative DOM X coordinate + * @param {number} domY graph-relative DOM Y coordinate + * Returns: {row, seriesName, point} + * @private + */ +Dygraph.prototype.findClosestPoint = function(domX, domY) { + var minDist = Infinity; + var dist, dx, dy, point, closestPoint, closestSeries, closestRow; + for ( var setIdx = this.layout_.points.length - 1 ; setIdx >= 0 ; --setIdx ) { + var points = this.layout_.points[setIdx]; + for (var i = 0; i < points.length; ++i) { + point = points[i]; + if (!utils.isValidPoint(point)) continue; + dx = point.canvasx - domX; + dy = point.canvasy - domY; + dist = dx * dx + dy * dy; + if (dist < minDist) { + minDist = dist; + closestPoint = point; + closestSeries = setIdx; + closestRow = point.idx; + } + } + } + var name = this.layout_.setNames[closestSeries]; + return { + row: closestRow, + seriesName: name, + point: closestPoint + }; +}; + +/** + * Given canvas X,Y coordinates, find the touched area in a stacked graph. + * + * This first finds the X data point closest to the supplied DOM X coordinate, + * then finds the series which puts the Y coordinate on top of its filled area, + * using linear interpolation between adjacent point pairs. + * + * @param {number} domX graph-relative DOM X coordinate + * @param {number} domY graph-relative DOM Y coordinate + * Returns: {row, seriesName, point} + * @private + */ +Dygraph.prototype.findStackedPoint = function(domX, domY) { + var row = this.findClosestRow(domX); + var closestPoint, closestSeries; + for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) { + var boundary = this.getLeftBoundary_(setIdx); + var rowIdx = row - boundary; + var points = this.layout_.points[setIdx]; + if (rowIdx >= points.length) continue; + var p1 = points[rowIdx]; + if (!utils.isValidPoint(p1)) continue; + var py = p1.canvasy; + if (domX > p1.canvasx && rowIdx + 1 < points.length) { + // interpolate series Y value using next point + var p2 = points[rowIdx + 1]; + if (utils.isValidPoint(p2)) { + var dx = p2.canvasx - p1.canvasx; + if (dx > 0) { + var r = (domX - p1.canvasx) / dx; + py += r * (p2.canvasy - p1.canvasy); + } + } + } else if (domX < p1.canvasx && rowIdx > 0) { + // interpolate series Y value using previous point + var p0 = points[rowIdx - 1]; + if (utils.isValidPoint(p0)) { + var dx = p1.canvasx - p0.canvasx; + if (dx > 0) { + var r = (p1.canvasx - domX) / dx; + py += r * (p0.canvasy - p1.canvasy); + } + } + } + // Stop if the point (domX, py) is above this series' upper edge + if (setIdx === 0 || py < domY) { + closestPoint = p1; + closestSeries = setIdx; + } + } + var name = this.layout_.setNames[closestSeries]; + return { + row: row, + seriesName: name, + point: closestPoint + }; +}; + +/** + * When the mouse moves in the canvas, display information about a nearby data + * point and draw dots over those points in the data series. This function + * takes care of cleanup of previously-drawn dots. + * @param {Object} event The mousemove event from the browser. + * @private + */ +Dygraph.prototype.mouseMove_ = function(event) { + // This prevents JS errors when mousing over the canvas before data loads. + var points = this.layout_.points; + if (points === undefined || points === null) return; + + var canvasCoords = this.eventToDomCoords(event); + var canvasx = canvasCoords[0]; + var canvasy = canvasCoords[1]; + + var highlightSeriesOpts = this.getOption("highlightSeriesOpts"); + var selectionChanged = false; + if (highlightSeriesOpts && !this.isSeriesLocked()) { + var closest; + if (this.getBooleanOption("stackedGraph")) { + closest = this.findStackedPoint(canvasx, canvasy); + } else { + closest = this.findClosestPoint(canvasx, canvasy); + } + selectionChanged = this.setSelection(closest.row, closest.seriesName); + } else { + var idx = this.findClosestRow(canvasx); + selectionChanged = this.setSelection(idx); + } + + var callback = this.getFunctionOption("highlightCallback"); + if (callback && selectionChanged) { + callback.call(this, event, + this.lastx_, + this.selPoints_, + this.lastRow_, + this.highlightSet_); + } +}; + +/** + * Fetch left offset from the specified set index or if not passed, the + * first defined boundaryIds record (see bug #236). + * @private + */ +Dygraph.prototype.getLeftBoundary_ = function(setIdx) { + if (this.boundaryIds_[setIdx]) { + return this.boundaryIds_[setIdx][0]; + } else { + for (var i = 0; i < this.boundaryIds_.length; i++) { + if (this.boundaryIds_[i] !== undefined) { + return this.boundaryIds_[i][0]; + } + } + return 0; + } +}; + +Dygraph.prototype.animateSelection_ = function(direction) { + var totalSteps = 10; + var millis = 30; + if (this.fadeLevel === undefined) this.fadeLevel = 0; + if (this.animateId === undefined) this.animateId = 0; + var start = this.fadeLevel; + var steps = direction < 0 ? start : totalSteps - start; + if (steps <= 0) { + if (this.fadeLevel) { + this.updateSelection_(1.0); + } + return; + } + + var thisId = ++this.animateId; + var that = this; + var cleanupIfClearing = function() { + // if we haven't reached fadeLevel 0 in the max frame time, + // ensure that the clear happens and just go to 0 + if (that.fadeLevel !== 0 && direction < 0) { + that.fadeLevel = 0; + that.clearSelection(); + } + }; + utils.repeatAndCleanup( + function(n) { + // ignore simultaneous animations + if (that.animateId != thisId) return; + + that.fadeLevel += direction; + if (that.fadeLevel === 0) { + that.clearSelection(); + } else { + that.updateSelection_(that.fadeLevel / totalSteps); + } + }, + steps, millis, cleanupIfClearing); +}; + +/** + * Draw dots over the selectied points in the data series. This function + * takes care of cleanup of previously-drawn dots. + * @private + */ +Dygraph.prototype.updateSelection_ = function(opt_animFraction) { + /*var defaultPrevented = */ + this.cascadeEvents_('select', { + selectedRow: this.lastRow_ === -1 ? undefined : this.lastRow_, + selectedX: this.lastx_ === -1 ? undefined : this.lastx_, + selectedPoints: this.selPoints_ + }); + // TODO(danvk): use defaultPrevented here? + + // Clear the previously drawn vertical, if there is one + var i; + var ctx = this.canvas_ctx_; + if (this.getOption('highlightSeriesOpts')) { + ctx.clearRect(0, 0, this.width_, this.height_); + var alpha = 1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha'); + var backgroundColor = utils.toRGB_(this.getOption('highlightSeriesBackgroundColor')); + + if (alpha) { + // Activating background fade includes an animation effect for a gradual + // fade. TODO(klausw): make this independently configurable if it causes + // issues? Use a shared preference to control animations? + var animateBackgroundFade = true; + if (animateBackgroundFade) { + if (opt_animFraction === undefined) { + // start a new animation + this.animateSelection_(1); + return; + } + alpha *= opt_animFraction; + } + ctx.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + alpha + ')'; + ctx.fillRect(0, 0, this.width_, this.height_); + } + + // Redraw only the highlighted series in the interactive canvas (not the + // static plot canvas, which is where series are usually drawn). + this.plotter_._renderLineChart(this.highlightSet_, ctx); + } else if (this.previousVerticalX_ >= 0) { + // Determine the maximum highlight circle size. + var maxCircleSize = 0; + var labels = this.attr_('labels'); + for (i = 1; i < labels.length; i++) { + var r = this.getNumericOption('highlightCircleSize', labels[i]); + if (r > maxCircleSize) maxCircleSize = r; + } + var px = this.previousVerticalX_; + ctx.clearRect(px - maxCircleSize - 1, 0, + 2 * maxCircleSize + 2, this.height_); + } + + if (this.selPoints_.length > 0) { + // Draw colored circles over the center of each selected point + var canvasx = this.selPoints_[0].canvasx; + ctx.save(); + for (i = 0; i < this.selPoints_.length; i++) { + var pt = this.selPoints_[i]; + if (isNaN(pt.canvasy)) continue; + + var circleSize = this.getNumericOption('highlightCircleSize', pt.name); + var callback = this.getFunctionOption("drawHighlightPointCallback", pt.name); + var color = this.plotter_.colors[pt.name]; + if (!callback) { + callback = utils.Circles.DEFAULT; + } + ctx.lineWidth = this.getNumericOption('strokeWidth', pt.name); + ctx.strokeStyle = color; + ctx.fillStyle = color; + callback.call(this, this, pt.name, ctx, canvasx, pt.canvasy, + color, circleSize, pt.idx); + } + ctx.restore(); + + this.previousVerticalX_ = canvasx; + } +}; + +/** + * Manually set the selected points and display information about them in the + * legend. The selection can be cleared using clearSelection() and queried + * using getSelection(). + * + * To set a selected series but not a selected point, call setSelection with + * row=false and the selected series name. + * + * @param {number} row Row number that should be highlighted (i.e. appear with + * hover dots on the chart). + * @param {seriesName} optional series name to highlight that series with the + * the highlightSeriesOpts setting. + * @param { locked } optional If true, keep seriesName selected when mousing + * over the graph, disabling closest-series highlighting. Call clearSelection() + * to unlock it. + */ +Dygraph.prototype.setSelection = function(row, opt_seriesName, opt_locked) { + // Extract the points we've selected + this.selPoints_ = []; + + var changed = false; + if (row !== false && row >= 0) { + if (row != this.lastRow_) changed = true; + this.lastRow_ = row; + for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) { + var points = this.layout_.points[setIdx]; + // Check if the point at the appropriate index is the point we're looking + // for. If it is, just use it, otherwise search the array for a point + // in the proper place. + var setRow = row - this.getLeftBoundary_(setIdx); + if (setRow >= 0 && setRow < points.length && points[setRow].idx == row) { + var point = points[setRow]; + if (point.yval !== null) this.selPoints_.push(point); + } else { + for (var pointIdx = 0; pointIdx < points.length; ++pointIdx) { + var point = points[pointIdx]; + if (point.idx == row) { + if (point.yval !== null) { + this.selPoints_.push(point); + } + break; + } + } + } + } + } else { + if (this.lastRow_ >= 0) changed = true; + this.lastRow_ = -1; + } + + if (this.selPoints_.length) { + this.lastx_ = this.selPoints_[0].xval; + } else { + this.lastx_ = -1; + } + + if (opt_seriesName !== undefined) { + if (this.highlightSet_ !== opt_seriesName) changed = true; + this.highlightSet_ = opt_seriesName; + } + + if (opt_locked !== undefined) { + this.lockedSet_ = opt_locked; + } + + if (changed) { + this.updateSelection_(undefined); + } + return changed; +}; + +/** + * The mouse has left the canvas. Clear out whatever artifacts remain + * @param {Object} event the mouseout event from the browser. + * @private + */ +Dygraph.prototype.mouseOut_ = function(event) { + if (this.getFunctionOption("unhighlightCallback")) { + this.getFunctionOption("unhighlightCallback").call(this, event); + } + + if (this.getBooleanOption("hideOverlayOnMouseOut") && !this.lockedSet_) { + this.clearSelection(); + } +}; + +/** + * Clears the current selection (i.e. points that were highlighted by moving + * the mouse over the chart). + */ +Dygraph.prototype.clearSelection = function() { + this.cascadeEvents_('deselect', {}); + + this.lockedSet_ = false; + // Get rid of the overlay data + if (this.fadeLevel) { + this.animateSelection_(-1); + return; + } + this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_); + this.fadeLevel = 0; + this.selPoints_ = []; + this.lastx_ = -1; + this.lastRow_ = -1; + this.highlightSet_ = null; +}; + +/** + * Returns the number of the currently selected row. To get data for this row, + * you can use the getValue method. + * @return {number} row number, or -1 if nothing is selected + */ +Dygraph.prototype.getSelection = function() { + if (!this.selPoints_ || this.selPoints_.length < 1) { + return -1; + } + + for (var setIdx = 0; setIdx < this.layout_.points.length; setIdx++) { + var points = this.layout_.points[setIdx]; + for (var row = 0; row < points.length; row++) { + if (points[row].x == this.selPoints_[0].x) { + return points[row].idx; + } + } + } + return -1; +}; + +/** + * Returns the name of the currently-highlighted series. + * Only available when the highlightSeriesOpts option is in use. + */ +Dygraph.prototype.getHighlightSeries = function() { + return this.highlightSet_; +}; + +/** + * Returns true if the currently-highlighted series was locked + * via setSelection(..., seriesName, true). + */ +Dygraph.prototype.isSeriesLocked = function() { + return this.lockedSet_; +}; + +/** + * Fires when there's data available to be graphed. + * @param {string} data Raw CSV data to be plotted + * @private + */ +Dygraph.prototype.loadedEvent_ = function(data) { + this.rawData_ = this.parseCSV_(data); + this.cascadeDataDidUpdateEvent_(); + this.predraw_(); +}; + +/** + * Add ticks on the x-axis representing years, months, quarters, weeks, or days + * @private + */ +Dygraph.prototype.addXTicks_ = function() { + // Determine the correct ticks scale on the x-axis: quarterly, monthly, ... + var range; + if (this.dateWindow_) { + range = [this.dateWindow_[0], this.dateWindow_[1]]; + } else { + range = this.xAxisExtremes(); + } + + var xAxisOptionsView = this.optionsViewForAxis_('x'); + var xTicks = xAxisOptionsView('ticker')( + range[0], + range[1], + this.plotter_.area.w, // TODO(danvk): should be area.width + xAxisOptionsView, + this); + // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks); + // console.log(msg); + this.layout_.setXTicks(xTicks); +}; + +/** + * Returns the correct handler class for the currently set options. + * @private + */ +Dygraph.prototype.getHandlerClass_ = function() { + var handlerClass; + if (this.attr_('dataHandler')) { + handlerClass = this.attr_('dataHandler'); + } else if (this.fractions_) { + if (this.getBooleanOption('errorBars')) { + handlerClass = FractionsBarsHandler; + } else { + handlerClass = DefaultFractionHandler; + } + } else if (this.getBooleanOption('customBars')) { + handlerClass = CustomBarsHandler; + } else if (this.getBooleanOption('errorBars')) { + handlerClass = ErrorBarsHandler; + } else { + handlerClass = DefaultHandler; + } + return handlerClass; +}; + +/** + * @private + * This function is called once when the chart's data is changed or the options + * dictionary is updated. It is _not_ called when the user pans or zooms. The + * idea is that values derived from the chart's data can be computed here, + * rather than every time the chart is drawn. This includes things like the + * number of axes, rolling averages, etc. + */ +Dygraph.prototype.predraw_ = function() { + var start = new Date(); + + // Create the correct dataHandler + this.dataHandler_ = new (this.getHandlerClass_())(); + + this.layout_.computePlotArea(); + + // TODO(danvk): move more computations out of drawGraph_ and into here. + this.computeYAxes_(); + + if (!this.is_initial_draw_) { + this.canvas_ctx_.restore(); + this.hidden_ctx_.restore(); + } + + this.canvas_ctx_.save(); + this.hidden_ctx_.save(); + + // Create a new plotter. + this.plotter_ = new DygraphCanvasRenderer(this, + this.hidden_, + this.hidden_ctx_, + this.layout_); + + // The roller sits in the bottom left corner of the chart. We don't know where + // this will be until the options are available, so it's positioned here. + this.createRollInterface_(); + + this.cascadeEvents_('predraw'); + + // Convert the raw data (a 2D array) into the internal format and compute + // rolling averages. + this.rolledSeries_ = [null]; // x-axis is the first series and it's special + for (var i = 1; i < this.numColumns(); i++) { + // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too. + var series = this.dataHandler_.extractSeries(this.rawData_, i, this.attributes_); + if (this.rollPeriod_ > 1) { + series = this.dataHandler_.rollingAverage(series, this.rollPeriod_, this.attributes_); + } + + this.rolledSeries_.push(series); + } + + // If the data or options have changed, then we'd better redraw. + this.drawGraph_(); + + // This is used to determine whether to do various animations. + var end = new Date(); + this.drawingTimeMs_ = (end - start); +}; + +/** + * Point structure. + * + * xval_* and yval_* are the original unscaled data values, + * while x_* and y_* are scaled to the range (0.0-1.0) for plotting. + * yval_stacked is the cumulative Y value used for stacking graphs, + * and bottom/top/minus/plus are used for error bar graphs. + * + * @typedef {{ + * idx: number, + * name: string, + * x: ?number, + * xval: ?number, + * y_bottom: ?number, + * y: ?number, + * y_stacked: ?number, + * y_top: ?number, + * yval_minus: ?number, + * yval: ?number, + * yval_plus: ?number, + * yval_stacked + * }} + */ +Dygraph.PointType = undefined; + +/** + * Calculates point stacking for stackedGraph=true. + * + * For stacking purposes, interpolate or extend neighboring data across + * NaN values based on stackedGraphNaNFill settings. This is for display + * only, the underlying data value as shown in the legend remains NaN. + * + * @param {Array.} points Point array for a single series. + * Updates each Point's yval_stacked property. + * @param {Array.} cumulativeYval Accumulated top-of-graph stacked Y + * values for the series seen so far. Index is the row number. Updated + * based on the current series's values. + * @param {Array.} seriesExtremes Min and max values, updated + * to reflect the stacked values. + * @param {string} fillMethod Interpolation method, one of 'all', 'inside', or + * 'none'. + * @private + */ +Dygraph.stackPoints_ = function( + points, cumulativeYval, seriesExtremes, fillMethod) { + var lastXval = null; + var prevPoint = null; + var nextPoint = null; + var nextPointIdx = -1; + + // Find the next stackable point starting from the given index. + var updateNextPoint = function(idx) { + // If we've previously found a non-NaN point and haven't gone past it yet, + // just use that. + if (nextPointIdx >= idx) return; + + // We haven't found a non-NaN point yet or have moved past it, + // look towards the right to find a non-NaN point. + for (var j = idx; j < points.length; ++j) { + // Clear out a previously-found point (if any) since it's no longer + // valid, we shouldn't use it for interpolation anymore. + nextPoint = null; + if (!isNaN(points[j].yval) && points[j].yval !== null) { + nextPointIdx = j; + nextPoint = points[j]; + break; + } + } + }; + + for (var i = 0; i < points.length; ++i) { + var point = points[i]; + var xval = point.xval; + if (cumulativeYval[xval] === undefined) { + cumulativeYval[xval] = 0; + } + + var actualYval = point.yval; + if (isNaN(actualYval) || actualYval === null) { + if(fillMethod == 'none') { + actualYval = 0; + } else { + // Interpolate/extend for stacking purposes if possible. + updateNextPoint(i); + if (prevPoint && nextPoint && fillMethod != 'none') { + // Use linear interpolation between prevPoint and nextPoint. + actualYval = prevPoint.yval + (nextPoint.yval - prevPoint.yval) * + ((xval - prevPoint.xval) / (nextPoint.xval - prevPoint.xval)); + } else if (prevPoint && fillMethod == 'all') { + actualYval = prevPoint.yval; + } else if (nextPoint && fillMethod == 'all') { + actualYval = nextPoint.yval; + } else { + actualYval = 0; + } + } + } else { + prevPoint = point; + } + + var stackedYval = cumulativeYval[xval]; + if (lastXval != xval) { + // If an x-value is repeated, we ignore the duplicates. + stackedYval += actualYval; + cumulativeYval[xval] = stackedYval; + } + lastXval = xval; + + point.yval_stacked = stackedYval; + + if (stackedYval > seriesExtremes[1]) { + seriesExtremes[1] = stackedYval; + } + if (stackedYval < seriesExtremes[0]) { + seriesExtremes[0] = stackedYval; + } + } +}; + + +/** + * Loop over all fields and create datasets, calculating extreme y-values for + * each series and extreme x-indices as we go. + * + * dateWindow is passed in as an explicit parameter so that we can compute + * extreme values "speculatively", i.e. without actually setting state on the + * dygraph. + * + * @param {Array.)>>} rolledSeries, where + * rolledSeries[seriesIndex][row] = raw point, where + * seriesIndex is the column number starting with 1, and + * rawPoint is [x,y] or [x, [y, err]] or [x, [y, yminus, yplus]]. + * @param {?Array.} dateWindow [xmin, xmax] pair, or null. + * @return {{ + * points: Array.>, + * seriesExtremes: Array.>, + * boundaryIds: Array.}} + * @private + */ +Dygraph.prototype.gatherDatasets_ = function(rolledSeries, dateWindow) { + var boundaryIds = []; + var points = []; + var cumulativeYval = []; // For stacked series. + var extremes = {}; // series name -> [low, high] + var seriesIdx, sampleIdx; + var firstIdx, lastIdx; + var axisIdx; + + // Loop over the fields (series). Go from the last to the first, + // because if they're stacked that's how we accumulate the values. + var num_series = rolledSeries.length - 1; + var series; + for (seriesIdx = num_series; seriesIdx >= 1; seriesIdx--) { + if (!this.visibility()[seriesIdx - 1]) continue; + + // Prune down to the desired range, if necessary (for zooming) + // Because there can be lines going to points outside of the visible area, + // we actually prune to visible points, plus one on either side. + if (dateWindow) { + series = rolledSeries[seriesIdx]; + var low = dateWindow[0]; + var high = dateWindow[1]; + + // TODO(danvk): do binary search instead of linear search. + // TODO(danvk): pass firstIdx and lastIdx directly to the renderer. + firstIdx = null; + lastIdx = null; + for (sampleIdx = 0; sampleIdx < series.length; sampleIdx++) { + if (series[sampleIdx][0] >= low && firstIdx === null) { + firstIdx = sampleIdx; + } + if (series[sampleIdx][0] <= high) { + lastIdx = sampleIdx; + } + } + + if (firstIdx === null) firstIdx = 0; + var correctedFirstIdx = firstIdx; + var isInvalidValue = true; + while (isInvalidValue && correctedFirstIdx > 0) { + correctedFirstIdx--; + // check if the y value is null. + isInvalidValue = series[correctedFirstIdx][1] === null; + } + + if (lastIdx === null) lastIdx = series.length - 1; + var correctedLastIdx = lastIdx; + isInvalidValue = true; + while (isInvalidValue && correctedLastIdx < series.length - 1) { + correctedLastIdx++; + isInvalidValue = series[correctedLastIdx][1] === null; + } + + if (correctedFirstIdx!==firstIdx) { + firstIdx = correctedFirstIdx; + } + if (correctedLastIdx !== lastIdx) { + lastIdx = correctedLastIdx; + } + + boundaryIds[seriesIdx-1] = [firstIdx, lastIdx]; + + // .slice's end is exclusive, we want to include lastIdx. + series = series.slice(firstIdx, lastIdx + 1); + } else { + series = rolledSeries[seriesIdx]; + boundaryIds[seriesIdx-1] = [0, series.length-1]; + } + + var seriesName = this.attr_("labels")[seriesIdx]; + var seriesExtremes = this.dataHandler_.getExtremeYValues(series, + dateWindow, this.getBooleanOption("stepPlot",seriesName)); + + var seriesPoints = this.dataHandler_.seriesToPoints(series, + seriesName, boundaryIds[seriesIdx-1][0]); + + if (this.getBooleanOption("stackedGraph")) { + axisIdx = this.attributes_.axisForSeries(seriesName); + if (cumulativeYval[axisIdx] === undefined) { + cumulativeYval[axisIdx] = []; + } + Dygraph.stackPoints_(seriesPoints, cumulativeYval[axisIdx], seriesExtremes, + this.getBooleanOption("stackedGraphNaNFill")); + } + + extremes[seriesName] = seriesExtremes; + points[seriesIdx] = seriesPoints; + } + + return { points: points, extremes: extremes, boundaryIds: boundaryIds }; +}; + +/** + * Update the graph with new data. This method is called when the viewing area + * has changed. If the underlying data or options have changed, predraw_ will + * be called before drawGraph_ is called. + * + * @private + */ +Dygraph.prototype.drawGraph_ = function() { + var start = new Date(); + + // This is used to set the second parameter to drawCallback, below. + var is_initial_draw = this.is_initial_draw_; + this.is_initial_draw_ = false; + + this.layout_.removeAllDatasets(); + this.setColors_(); + this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize'); + + var packed = this.gatherDatasets_(this.rolledSeries_, this.dateWindow_); + var points = packed.points; + var extremes = packed.extremes; + this.boundaryIds_ = packed.boundaryIds; + + this.setIndexByName_ = {}; + var labels = this.attr_("labels"); + var dataIdx = 0; + for (var i = 1; i < points.length; i++) { + if (!this.visibility()[i - 1]) continue; + this.layout_.addDataset(labels[i], points[i]); + this.datasetIndex_[i] = dataIdx++; + } + for (var i = 0; i < labels.length; i++) { + this.setIndexByName_[labels[i]] = i; + } + + this.computeYAxisRanges_(extremes); + this.layout_.setYAxes(this.axes_); + + this.addXTicks_(); + + // Tell PlotKit to use this new data and render itself + this.layout_.evaluate(); + this.renderGraph_(is_initial_draw); + + if (this.getStringOption("timingName")) { + var end = new Date(); + console.log(this.getStringOption("timingName") + " - drawGraph: " + (end - start) + "ms"); + } +}; + +/** + * This does the work of drawing the chart. It assumes that the layout and axis + * scales have already been set (e.g. by predraw_). + * + * @private + */ +Dygraph.prototype.renderGraph_ = function(is_initial_draw) { + this.cascadeEvents_('clearChart'); + this.plotter_.clear(); + + const underlayCallback = this.getFunctionOption('underlayCallback'); + if (underlayCallback) { + // NOTE: we pass the dygraph object to this callback twice to avoid breaking + // users who expect a deprecated form of this callback. + underlayCallback.call(this, + this.hidden_ctx_, this.layout_.getPlotArea(), this, this); + } + + var e = { + canvas: this.hidden_, + drawingContext: this.hidden_ctx_ + }; + this.cascadeEvents_('willDrawChart', e); + this.plotter_.render(); + this.cascadeEvents_('didDrawChart', e); + this.lastRow_ = -1; // because plugins/legend.js clears the legend + + // TODO(danvk): is this a performance bottleneck when panning? + // The interaction canvas should already be empty in that situation. + this.canvas_.getContext('2d').clearRect(0, 0, this.width_, this.height_); + + const drawCallback = this.getFunctionOption("drawCallback"); + if (drawCallback !== null) { + drawCallback.call(this, this, is_initial_draw); + } + if (is_initial_draw) { + this.readyFired_ = true; + while (this.readyFns_.length > 0) { + var fn = this.readyFns_.pop(); + fn(this); + } + } +}; + +/** + * @private + * Determine properties of the y-axes which are independent of the data + * currently being displayed. This includes things like the number of axes and + * the style of the axes. It does not include the range of each axis and its + * tick marks. + * This fills in this.axes_. + * axes_ = [ { options } ] + * indices are into the axes_ array. + */ +Dygraph.prototype.computeYAxes_ = function() { + var axis, index, opts, v; + + // this.axes_ doesn't match this.attributes_.axes_.options. It's used for + // data computation as well as options storage. + // Go through once and add all the axes. + this.axes_ = []; + + for (axis = 0; axis < this.attributes_.numAxes(); axis++) { + // Add a new axis, making a copy of its per-axis options. + opts = { g : this }; + utils.update(opts, this.attributes_.axisOptions(axis)); + this.axes_[axis] = opts; + } + + for (axis = 0; axis < this.axes_.length; axis++) { + if (axis === 0) { + opts = this.optionsViewForAxis_('y' + (axis ? '2' : '')); + v = opts("valueRange"); + if (v) this.axes_[axis].valueRange = v; + } else { // To keep old behavior + var axes = this.user_attrs_.axes; + if (axes && axes.y2) { + v = axes.y2.valueRange; + if (v) this.axes_[axis].valueRange = v; + } + } + } +}; + +/** + * Returns the number of y-axes on the chart. + * @return {number} the number of axes. + */ +Dygraph.prototype.numAxes = function() { + return this.attributes_.numAxes(); +}; + +/** + * @private + * Returns axis properties for the given series. + * @param {string} setName The name of the series for which to get axis + * properties, e.g. 'Y1'. + * @return {Object} The axis properties. + */ +Dygraph.prototype.axisPropertiesForSeries = function(series) { + // TODO(danvk): handle errors. + return this.axes_[this.attributes_.axisForSeries(series)]; +}; + +/** + * @private + * Determine the value range and tick marks for each axis. + * @param {Object} extremes A mapping from seriesName -> [low, high] + * This fills in the valueRange and ticks fields in each entry of this.axes_. + */ +Dygraph.prototype.computeYAxisRanges_ = function(extremes) { + var isNullUndefinedOrNaN = function(num) { + return isNaN(parseFloat(num)); + }; + var numAxes = this.attributes_.numAxes(); + var ypadCompat, span, series, ypad; + + var p_axis; + + // Compute extreme values, a span and tick marks for each axis. + for (var i = 0; i < numAxes; i++) { + var axis = this.axes_[i]; + var logscale = this.attributes_.getForAxis("logscale", i); + var includeZero = this.attributes_.getForAxis("includeZero", i); + var independentTicks = this.attributes_.getForAxis("independentTicks", i); + series = this.attributes_.seriesForAxis(i); + + // Add some padding. This supports two Y padding operation modes: + // + // - backwards compatible (yRangePad not set): + // 10% padding for automatic Y ranges, but not for user-supplied + // ranges, and move a close-to-zero edge to zero, since drawing at the edge + // results in invisible lines. Unfortunately lines drawn at the edge of a + // user-supplied range will still be invisible. If logscale is + // set, add a variable amount of padding at the top but + // none at the bottom. + // + // - new-style (yRangePad set by the user): + // always add the specified Y padding. + // + ypadCompat = true; + ypad = 0.1; // add 10% + const yRangePad = this.getNumericOption('yRangePad'); + if (yRangePad !== null) { + ypadCompat = false; + // Convert pixel padding to ratio + ypad = yRangePad / this.plotter_.area.h; + } + + if (series.length === 0) { + // If no series are defined or visible then use a reasonable default + axis.extremeRange = [0, 1]; + } else { + // Calculate the extremes of extremes. + var minY = Infinity; // extremes[series[0]][0]; + var maxY = -Infinity; // extremes[series[0]][1]; + var extremeMinY, extremeMaxY; + + for (var j = 0; j < series.length; j++) { + // this skips invisible series + if (!extremes.hasOwnProperty(series[j])) continue; + + // Only use valid extremes to stop null data series' from corrupting the scale. + extremeMinY = extremes[series[j]][0]; + if (extremeMinY !== null) { + minY = Math.min(extremeMinY, minY); + } + extremeMaxY = extremes[series[j]][1]; + if (extremeMaxY !== null) { + maxY = Math.max(extremeMaxY, maxY); + } + } + + // Include zero if requested by the user. + if (includeZero && !logscale) { + if (minY > 0) minY = 0; + if (maxY < 0) maxY = 0; + } + + // Ensure we have a valid scale, otherwise default to [0, 1] for safety. + if (minY == Infinity) minY = 0; + if (maxY == -Infinity) maxY = 1; + + span = maxY - minY; + // special case: if we have no sense of scale, center on the sole value. + if (span === 0) { + if (maxY !== 0) { + span = Math.abs(maxY); + } else { + // ... and if the sole value is zero, use range 0-1. + maxY = 1; + span = 1; + } + } + + var maxAxisY = maxY, minAxisY = minY; + if (ypadCompat) { + if (logscale) { + maxAxisY = maxY + ypad * span; + minAxisY = minY; + } else { + maxAxisY = maxY + ypad * span; + minAxisY = minY - ypad * span; + + // Backwards-compatible behavior: Move the span to start or end at zero if it's + // close to zero. + if (minAxisY < 0 && minY >= 0) minAxisY = 0; + if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0; + } + } + axis.extremeRange = [minAxisY, maxAxisY]; + } + if (axis.valueRange) { + // This is a user-set value range for this axis. + var y0 = isNullUndefinedOrNaN(axis.valueRange[0]) ? axis.extremeRange[0] : axis.valueRange[0]; + var y1 = isNullUndefinedOrNaN(axis.valueRange[1]) ? axis.extremeRange[1] : axis.valueRange[1]; + axis.computedValueRange = [y0, y1]; + } else { + axis.computedValueRange = axis.extremeRange; + } + if (!ypadCompat) { + // When using yRangePad, adjust the upper/lower bounds to add + // padding unless the user has zoomed/panned the Y axis range. + + y0 = axis.computedValueRange[0]; + y1 = axis.computedValueRange[1]; + + // special case #781: if we have no sense of scale, center on the sole value. + if (y0 === y1) { + y0 -= 0.5; + y1 += 0.5; + } + + if (logscale) { + var y0pct = ypad / (2 * ypad - 1); + var y1pct = (ypad - 1) / (2 * ypad - 1); + axis.computedValueRange[0] = utils.logRangeFraction(y0, y1, y0pct); + axis.computedValueRange[1] = utils.logRangeFraction(y0, y1, y1pct); + } else { + span = y1 - y0; + axis.computedValueRange[0] = y0 - span * ypad; + axis.computedValueRange[1] = y1 + span * ypad; + } + } + + + if (independentTicks) { + axis.independentTicks = independentTicks; + var opts = this.optionsViewForAxis_('y' + (i ? '2' : '')); + var ticker = opts('ticker'); + axis.ticks = ticker(axis.computedValueRange[0], + axis.computedValueRange[1], + this.plotter_.area.h, + opts, + this); + // Define the first independent axis as primary axis. + if (!p_axis) p_axis = axis; + } + } + if (p_axis === undefined) { + throw ("Configuration Error: At least one axis has to have the \"independentTicks\" option activated."); + } + // Add ticks. By default, all axes inherit the tick positions of the + // primary axis. However, if an axis is specifically marked as having + // independent ticks, then that is permissible as well. + for (var i = 0; i < numAxes; i++) { + var axis = this.axes_[i]; + + if (!axis.independentTicks) { + var opts = this.optionsViewForAxis_('y' + (i ? '2' : '')); + var ticker = opts('ticker'); + var p_ticks = p_axis.ticks; + var p_scale = p_axis.computedValueRange[1] - p_axis.computedValueRange[0]; + var scale = axis.computedValueRange[1] - axis.computedValueRange[0]; + var tick_values = []; + for (var k = 0; k < p_ticks.length; k++) { + var y_frac = (p_ticks[k].v - p_axis.computedValueRange[0]) / p_scale; + var y_val = axis.computedValueRange[0] + y_frac * scale; + tick_values.push(y_val); + } + + axis.ticks = ticker(axis.computedValueRange[0], + axis.computedValueRange[1], + this.plotter_.area.h, + opts, + this, + tick_values); + } + } +}; + +/** + * Detects the type of the str (date or numeric) and sets the various + * formatting attributes in this.attrs_ based on this type. + * @param {string} str An x value. + * @private + */ +Dygraph.prototype.detectTypeFromString_ = function(str) { + var isDate = false; + var dashPos = str.indexOf('-'); // could be 2006-01-01 _or_ 1.0e-2 + if ((dashPos > 0 && (str[dashPos-1] != 'e' && str[dashPos-1] != 'E')) || + str.indexOf('/') >= 0 || + isNaN(parseFloat(str))) { + isDate = true; + } else if (str.length == 8 && str > '19700101' && str < '20371231') { + // TODO(danvk): remove support for this format. + isDate = true; + } + + this.setXAxisOptions_(isDate); +}; + +Dygraph.prototype.setXAxisOptions_ = function(isDate) { + if (isDate) { + this.attrs_.xValueParser = utils.dateParser; + this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter; + this.attrs_.axes.x.ticker = DygraphTickers.dateTicker; + this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; + } else { + /** @private (shut up, jsdoc!) */ + this.attrs_.xValueParser = function(x) { return parseFloat(x); }; + // TODO(danvk): use Dygraph.numberValueFormatter here? + /** @private (shut up, jsdoc!) */ + this.attrs_.axes.x.valueFormatter = function(x) { return x; }; + this.attrs_.axes.x.ticker = DygraphTickers.numericTicks; + this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter; + } +}; + +/** + * @private + * Parses a string in a special csv format. We expect a csv file where each + * line is a date point, and the first field in each line is the date string. + * We also expect that all remaining fields represent series. + * if the errorBars attribute is set, then interpret the fields as: + * date, series1, stddev1, series2, stddev2, ... + * @param {[Object]} data See above. + * + * @return [Object] An array with one entry for each row. These entries + * are an array of cells in that row. The first entry is the parsed x-value for + * the row. The second, third, etc. are the y-values. These can take on one of + * three forms, depending on the CSV and constructor parameters: + * 1. numeric value + * 2. [ value, stddev ] + * 3. [ low value, center value, high value ] + */ +Dygraph.prototype.parseCSV_ = function(data) { + var ret = []; + var line_delimiter = utils.detectLineDelimiter(data); + var lines = data.split(line_delimiter || "\n"); + var vals, j; + + // Use the default delimiter or fall back to a tab if that makes sense. + var delim = this.getStringOption('delimiter'); + if (lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0) { + delim = '\t'; + } + + var start = 0; + if (!('labels' in this.user_attrs_)) { + // User hasn't explicitly set labels, so they're (presumably) in the CSV. + start = 1; + this.attrs_.labels = lines[0].split(delim); // NOTE: _not_ user_attrs_. + this.attributes_.reparseSeries(); + } + var line_no = 0; + + var xParser; + var defaultParserSet = false; // attempt to auto-detect x value type + var expectedCols = this.attr_("labels").length; + var outOfOrder = false; + for (var i = start; i < lines.length; i++) { + var line = lines[i]; + line_no = i; + if (line.length === 0) continue; // skip blank lines + if (line[0] == '#') continue; // skip comment lines + var inFields = line.split(delim); + if (inFields.length < 2) continue; + + var fields = []; + if (!defaultParserSet) { + this.detectTypeFromString_(inFields[0]); + xParser = this.getFunctionOption("xValueParser"); + defaultParserSet = true; + } + fields[0] = xParser(inFields[0], this); + + // If fractions are expected, parse the numbers as "A/B" + if (this.fractions_) { + for (j = 1; j < inFields.length; j++) { + // TODO(danvk): figure out an appropriate way to flag parse errors. + vals = inFields[j].split("/"); + if (vals.length != 2) { + console.error('Expected fractional "num/den" values in CSV data ' + + "but found a value '" + inFields[j] + "' on line " + + (1 + i) + " ('" + line + "') which is not of this form."); + fields[j] = [0, 0]; + } else { + fields[j] = [utils.parseFloat_(vals[0], i, line), + utils.parseFloat_(vals[1], i, line)]; + } + } + } else if (this.getBooleanOption("errorBars")) { + // If there are error bars, values are (value, stddev) pairs + if (inFields.length % 2 != 1) { + console.error('Expected alternating (value, stdev.) pairs in CSV data ' + + 'but line ' + (1 + i) + ' has an odd number of values (' + + (inFields.length - 1) + "): '" + line + "'"); + } + for (j = 1; j < inFields.length; j += 2) { + fields[(j + 1) / 2] = [utils.parseFloat_(inFields[j], i, line), + utils.parseFloat_(inFields[j + 1], i, line)]; + } + } else if (this.getBooleanOption("customBars")) { + // Bars are a low;center;high tuple + for (j = 1; j < inFields.length; j++) { + var val = inFields[j]; + if (/^ *$/.test(val)) { + fields[j] = [null, null, null]; + } else { + vals = val.split(";"); + if (vals.length == 3) { + fields[j] = [ utils.parseFloat_(vals[0], i, line), + utils.parseFloat_(vals[1], i, line), + utils.parseFloat_(vals[2], i, line) ]; + } else { + console.warn('When using customBars, values must be either blank ' + + 'or "low;center;high" tuples (got "' + val + + '" on line ' + (1+i)); + } + } + } + } else { + // Values are just numbers + for (j = 1; j < inFields.length; j++) { + fields[j] = utils.parseFloat_(inFields[j], i, line); + } + } + if (ret.length > 0 && fields[0] < ret[ret.length - 1][0]) { + outOfOrder = true; + } + + if (fields.length != expectedCols) { + console.error("Number of columns in line " + i + " (" + fields.length + + ") does not agree with number of labels (" + expectedCols + + ") " + line); + } + + // If the user specified the 'labels' option and none of the cells of the + // first row parsed correctly, then they probably double-specified the + // labels. We go with the values set in the option, discard this row and + // log a warning to the JS console. + if (i === 0 && this.attr_('labels')) { + var all_null = true; + for (j = 0; all_null && j < fields.length; j++) { + if (fields[j]) all_null = false; + } + if (all_null) { + console.warn("The dygraphs 'labels' option is set, but the first row " + + "of CSV data ('" + line + "') appears to also contain " + + "labels. Will drop the CSV labels and use the option " + + "labels."); + continue; + } + } + ret.push(fields); + } + + if (outOfOrder) { + console.warn("CSV is out of order; order it correctly to speed loading."); + ret.sort(function(a,b) { return a[0] - b[0]; }); + } + + return ret; +}; + +// In native format, all values must be dates or numbers. +// This check isn't perfect but will catch most mistaken uses of strings. +function validateNativeFormat(data) { + const firstRow = data[0]; + const firstX = firstRow[0]; + if (typeof firstX !== 'number' && !utils.isDateLike(firstX)) { + throw new Error(`Expected number or date but got ${typeof firstX}: ${firstX}.`); + } + for (let i = 1; i < firstRow.length; i++) { + const val = firstRow[i]; + if (val === null || val === undefined) continue; + if (typeof val === 'number') continue; + if (utils.isArrayLike(val)) continue; // e.g. error bars or custom bars. + throw new Error(`Expected number or array but got ${typeof val}: ${val}.`); + } +} + +/** + * The user has provided their data as a pre-packaged JS array. If the x values + * are numeric, this is the same as dygraphs' internal format. If the x values + * are dates, we need to convert them from Date objects to ms since epoch. + * @param {!Array} data + * @return {Object} data with numeric x values. + * @private + */ +Dygraph.prototype.parseArray_ = function(data) { + // Peek at the first x value to see if it's numeric. + if (data.length === 0) { + console.error("Can't plot empty data set"); + return null; + } + if (data[0].length === 0) { + console.error("Data set cannot contain an empty row"); + return null; + } + + validateNativeFormat(data); + + var i; + if (this.attr_("labels") === null) { + console.warn("Using default labels. Set labels explicitly via 'labels' " + + "in the options parameter"); + this.attrs_.labels = [ "X" ]; + for (i = 1; i < data[0].length; i++) { + this.attrs_.labels.push("Y" + i); // Not user_attrs_. + } + this.attributes_.reparseSeries(); + } else { + var num_labels = this.attr_("labels"); + if (num_labels.length != data[0].length) { + console.error("Mismatch between number of labels (" + num_labels + ")" + + " and number of columns in array (" + data[0].length + ")"); + return null; + } + } + + if (utils.isDateLike(data[0][0])) { + // Some intelligent defaults for a date x-axis. + this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter; + this.attrs_.axes.x.ticker = DygraphTickers.dateTicker; + this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; + + // Assume they're all dates. + var parsedData = utils.clone(data); + for (i = 0; i < data.length; i++) { + if (parsedData[i].length === 0) { + console.error("Row " + (1 + i) + " of data is empty"); + return null; + } + if (parsedData[i][0] === null || + typeof(parsedData[i][0].getTime) != 'function' || + isNaN(parsedData[i][0].getTime())) { + console.error("x value in row " + (1 + i) + " is not a Date"); + return null; + } + parsedData[i][0] = parsedData[i][0].getTime(); + } + return parsedData; + } else { + // Some intelligent defaults for a numeric x-axis. + /** @private (shut up, jsdoc!) */ + this.attrs_.axes.x.valueFormatter = function(x) { return x; }; + this.attrs_.axes.x.ticker = DygraphTickers.numericTicks; + this.attrs_.axes.x.axisLabelFormatter = utils.numberAxisLabelFormatter; + return data; + } +}; + +/** + * Parses a DataTable object from gviz. + * The data is expected to have a first column that is either a date or a + * number. All subsequent columns must be numbers. If there is a clear mismatch + * between this.xValueParser_ and the type of the first column, it will be + * fixed. Fills out rawData_. + * @param {!google.visualization.DataTable} data See above. + * @private + */ +Dygraph.prototype.parseDataTable_ = function(data) { + var shortTextForAnnotationNum = function(num) { + // converts [0-9]+ [A-Z][a-z]* + // example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab + // and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz + var shortText = String.fromCharCode(65 /* A */ + num % 26); + num = Math.floor(num / 26); + while ( num > 0 ) { + shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26 ) + shortText.toLowerCase(); + num = Math.floor((num - 1) / 26); + } + return shortText; + }; + + var cols = data.getNumberOfColumns(); + var rows = data.getNumberOfRows(); + + var indepType = data.getColumnType(0); + if (indepType == 'date' || indepType == 'datetime') { + this.attrs_.xValueParser = utils.dateParser; + this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter; + this.attrs_.axes.x.ticker = DygraphTickers.dateTicker; + this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; + } else if (indepType == 'number') { + this.attrs_.xValueParser = function(x) { return parseFloat(x); }; + this.attrs_.axes.x.valueFormatter = function(x) { return x; }; + this.attrs_.axes.x.ticker = DygraphTickers.numericTicks; + this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter; + } else { + throw new Error( + "only 'date', 'datetime' and 'number' types are supported " + + "for column 1 of DataTable input (Got '" + indepType + "')"); + } + + // Array of the column indices which contain data (and not annotations). + var colIdx = []; + var annotationCols = {}; // data index -> [annotation cols] + var hasAnnotations = false; + var i, j; + for (i = 1; i < cols; i++) { + var type = data.getColumnType(i); + if (type == 'number') { + colIdx.push(i); + } else if (type == 'string' && this.getBooleanOption('displayAnnotations')) { + // This is OK -- it's an annotation column. + var dataIdx = colIdx[colIdx.length - 1]; + if (!annotationCols.hasOwnProperty(dataIdx)) { + annotationCols[dataIdx] = [i]; + } else { + annotationCols[dataIdx].push(i); + } + hasAnnotations = true; + } else { + throw new Error( + "Only 'number' is supported as a dependent type with Gviz." + + " 'string' is only supported if displayAnnotations is true"); + } + } + + // Read column labels + // TODO(danvk): add support back for errorBars + var labels = [data.getColumnLabel(0)]; + for (i = 0; i < colIdx.length; i++) { + labels.push(data.getColumnLabel(colIdx[i])); + if (this.getBooleanOption("errorBars")) i += 1; + } + this.attrs_.labels = labels; + cols = labels.length; + + var ret = []; + var outOfOrder = false; + var annotations = []; + for (i = 0; i < rows; i++) { + var row = []; + if (typeof(data.getValue(i, 0)) === 'undefined' || + data.getValue(i, 0) === null) { + console.warn("Ignoring row " + i + + " of DataTable because of undefined or null first column."); + continue; + } + + if (indepType == 'date' || indepType == 'datetime') { + row.push(data.getValue(i, 0).getTime()); + } else { + row.push(data.getValue(i, 0)); + } + if (!this.getBooleanOption("errorBars")) { + for (j = 0; j < colIdx.length; j++) { + var col = colIdx[j]; + row.push(data.getValue(i, col)); + if (hasAnnotations && + annotationCols.hasOwnProperty(col) && + data.getValue(i, annotationCols[col][0]) !== null) { + var ann = {}; + ann.series = data.getColumnLabel(col); + ann.xval = row[0]; + ann.shortText = shortTextForAnnotationNum(annotations.length); + ann.text = ''; + for (var k = 0; k < annotationCols[col].length; k++) { + if (k) ann.text += "\n"; + ann.text += data.getValue(i, annotationCols[col][k]); + } + annotations.push(ann); + } + } + + // Strip out infinities, which give dygraphs problems later on. + for (j = 0; j < row.length; j++) { + if (!isFinite(row[j])) row[j] = null; + } + } else { + for (j = 0; j < cols - 1; j++) { + row.push([ data.getValue(i, 1 + 2 * j), data.getValue(i, 2 + 2 * j) ]); + } + } + if (ret.length > 0 && row[0] < ret[ret.length - 1][0]) { + outOfOrder = true; + } + ret.push(row); + } + + if (outOfOrder) { + console.warn("DataTable is out of order; order it correctly to speed loading."); + ret.sort(function(a,b) { return a[0] - b[0]; }); + } + this.rawData_ = ret; + + if (annotations.length > 0) { + this.setAnnotations(annotations, true); + } + this.attributes_.reparseSeries(); +}; + +/** + * Signals to plugins that the chart data has updated. + * This happens after the data has updated but before the chart has redrawn. + * @private + */ +Dygraph.prototype.cascadeDataDidUpdateEvent_ = function() { + // TODO(danvk): there are some issues checking xAxisRange() and using + // toDomCoords from handlers of this event. The visible range should be set + // when the chart is drawn, not derived from the data. + this.cascadeEvents_('dataDidUpdate', {}); +}; + +/** + * Get the CSV data. If it's in a function, call that function. If it's in a + * file, do an XMLHttpRequest to get it. + * @private + */ +Dygraph.prototype.start_ = function() { + var data = this.file_; + + // Functions can return references of all other types. + if (typeof data == 'function') { + data = data(); + } + + if (utils.isArrayLike(data)) { + this.rawData_ = this.parseArray_(data); + this.cascadeDataDidUpdateEvent_(); + this.predraw_(); + } else if (typeof data == 'object' && + typeof data.getColumnRange == 'function') { + // must be a DataTable from gviz. + this.parseDataTable_(data); + this.cascadeDataDidUpdateEvent_(); + this.predraw_(); + } else if (typeof data == 'string') { + // Heuristic: a newline means it's CSV data. Otherwise it's an URL. + var line_delimiter = utils.detectLineDelimiter(data); + if (line_delimiter) { + this.loadedEvent_(data); + } else { + // REMOVE_FOR_IE + var req; + if (window.XMLHttpRequest) { + // Firefox, Opera, IE7, and other browsers will use the native object + req = new XMLHttpRequest(); + } else { + // IE 5 and 6 will use the ActiveX control + req = new ActiveXObject("Microsoft.XMLHTTP"); + } + + var caller = this; + req.onreadystatechange = function () { + if (req.readyState == 4) { + if (req.status === 200 || // Normal http + req.status === 0) { // Chrome w/ --allow-file-access-from-files + caller.loadedEvent_(req.responseText); + } + } + }; + + req.open("GET", data, true); + req.send(null); + } + } else { + console.error("Unknown data format: " + (typeof data)); + } +}; + +/** + * Changes various properties of the graph. These can include: + *
    + *
  • file: changes the source data for the graph
  • + *
  • errorBars: changes whether the data contains stddev
  • + *
+ * + * There's a huge variety of options that can be passed to this method. For a + * full list, see http://dygraphs.com/options.html. + * + * @param {Object} input_attrs The new properties and values + * @param {boolean} block_redraw Usually the chart is redrawn after every + * call to updateOptions(). If you know better, you can pass true to + * explicitly block the redraw. This can be useful for chaining + * updateOptions() calls, avoiding the occasional infinite loop and + * preventing redraws when it's not necessary (e.g. when updating a + * callback). + */ +Dygraph.prototype.updateOptions = function(input_attrs, block_redraw) { + if (typeof(block_redraw) == 'undefined') block_redraw = false; + + // copyUserAttrs_ drops the "file" parameter as a convenience to us. + var file = input_attrs.file; + var attrs = Dygraph.copyUserAttrs_(input_attrs); + + // TODO(danvk): this is a mess. Move these options into attr_. + if ('rollPeriod' in attrs) { + this.rollPeriod_ = attrs.rollPeriod; + } + if ('dateWindow' in attrs) { + this.dateWindow_ = attrs.dateWindow; + } + + // TODO(danvk): validate per-series options. + // Supported: + // strokeWidth + // pointSize + // drawPoints + // highlightCircleSize + + // Check if this set options will require new points. + var requiresNewPoints = utils.isPixelChangingOptionList(this.attr_("labels"), attrs); + + utils.updateDeep(this.user_attrs_, attrs); + + this.attributes_.reparseSeries(); + + if (file) { + // This event indicates that the data is about to change, but hasn't yet. + // TODO(danvk): support cancellation of the update via this event. + this.cascadeEvents_('dataWillUpdate', {}); + + this.file_ = file; + if (!block_redraw) this.start_(); + } else { + if (!block_redraw) { + if (requiresNewPoints) { + this.predraw_(); + } else { + this.renderGraph_(false); + } + } + } +}; + +/** + * Make a copy of input attributes, removing file as a convenience. + * @private + */ +Dygraph.copyUserAttrs_ = function(attrs) { + var my_attrs = {}; + for (var k in attrs) { + if (!attrs.hasOwnProperty(k)) continue; + if (k == 'file') continue; + if (attrs.hasOwnProperty(k)) my_attrs[k] = attrs[k]; + } + return my_attrs; +}; + +/** + * Resizes the dygraph. If no parameters are specified, resizes to fill the + * containing div (which has presumably changed size since the dygraph was + * instantiated. If the width/height are specified, the div will be resized. + * + * This is far more efficient than destroying and re-instantiating a + * Dygraph, since it doesn't have to reparse the underlying data. + * + * @param {number} width Width (in pixels) + * @param {number} height Height (in pixels) + */ +Dygraph.prototype.resize = function(width, height) { + if (this.resize_lock) { + return; + } + this.resize_lock = true; + + if ((width === null) != (height === null)) { + console.warn("Dygraph.resize() should be called with zero parameters or " + + "two non-NULL parameters. Pretending it was zero."); + width = height = null; + } + + var old_width = this.width_; + var old_height = this.height_; + + if (width) { + this.maindiv_.style.width = width + "px"; + this.maindiv_.style.height = height + "px"; + this.width_ = width; + this.height_ = height; + } else { + this.width_ = this.maindiv_.clientWidth; + this.height_ = this.maindiv_.clientHeight; + } + + if (old_width != this.width_ || old_height != this.height_) { + // Resizing a canvas erases it, even when the size doesn't change, so + // any resize needs to be followed by a redraw. + this.resizeElements_(); + this.predraw_(); + } + + this.resize_lock = false; +}; + +/** + * Adjusts the number of points in the rolling average. Updates the graph to + * reflect the new averaging period. + * @param {number} length Number of points over which to average the data. + */ +Dygraph.prototype.adjustRoll = function(length) { + this.rollPeriod_ = length; + this.predraw_(); +}; + +/** + * Returns a boolean array of visibility statuses. + */ +Dygraph.prototype.visibility = function() { + // Do lazy-initialization, so that this happens after we know the number of + // data series. + if (!this.getOption("visibility")) { + this.attrs_.visibility = []; + } + // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs. + while (this.getOption("visibility").length < this.numColumns() - 1) { + this.attrs_.visibility.push(true); + } + return this.getOption("visibility"); +}; + +/** + * Changes the visibility of one or more series. + * + * @param {number|number[]|object} num the series index or an array of series indices + * or a boolean array of visibility states by index + * or an object mapping series numbers, as keys, to + * visibility state (boolean values) + * @param {boolean} value the visibility state expressed as a boolean + */ +Dygraph.prototype.setVisibility = function(num, value) { + var x = this.visibility(); + var numIsObject = false; + + if (!Array.isArray(num)) { + if (num !== null && typeof num === 'object') { + numIsObject = true; + } else { + num = [num]; + } + } + + if (numIsObject) { + for (var i in num) { + if (num.hasOwnProperty(i)) { + if (i < 0 || i >= x.length) { + console.warn("Invalid series number in setVisibility: " + i); + } else { + x[i] = num[i]; + } + } + } + } else { + for (var i = 0; i < num.length; i++) { + if (typeof num[i] === 'boolean') { + if (i >= x.length) { + console.warn("Invalid series number in setVisibility: " + i); + } else { + x[i] = num[i]; + } + } else { + if (num[i] < 0 || num[i] >= x.length) { + console.warn("Invalid series number in setVisibility: " + num[i]); + } else { + x[num[i]] = value; + } + } + } + } + + this.predraw_(); +}; + +/** + * How large of an area will the dygraph render itself in? + * This is used for testing. + * @return A {width: w, height: h} object. + * @private + */ +Dygraph.prototype.size = function() { + return { width: this.width_, height: this.height_ }; +}; + +/** + * Update the list of annotations and redraw the chart. + * See dygraphs.com/annotations.html for more info on how to use annotations. + * @param ann {Array} An array of annotation objects. + * @param suppressDraw {Boolean} Set to "true" to block chart redraw (optional). + */ +Dygraph.prototype.setAnnotations = function(ann, suppressDraw) { + // Only add the annotation CSS rule once we know it will be used. + this.annotations_ = ann; + if (!this.layout_) { + console.warn("Tried to setAnnotations before dygraph was ready. " + + "Try setting them in a ready() block. See " + + "dygraphs.com/tests/annotation.html"); + return; + } + + this.layout_.setAnnotations(this.annotations_); + if (!suppressDraw) { + this.predraw_(); + } +}; + +/** + * Return the list of annotations. + */ +Dygraph.prototype.annotations = function() { + return this.annotations_; +}; + +/** + * Get the list of label names for this graph. The first column is the + * x-axis, so the data series names start at index 1. + * + * Returns null when labels have not yet been defined. + */ +Dygraph.prototype.getLabels = function() { + var labels = this.attr_("labels"); + return labels ? labels.slice() : null; +}; + +/** + * Get the index of a series (column) given its name. The first column is the + * x-axis, so the data series start with index 1. + */ +Dygraph.prototype.indexFromSetName = function(name) { + return this.setIndexByName_[name]; +}; + +/** + * Find the row number corresponding to the given x-value. + * Returns null if there is no such x-value in the data. + * If there are multiple rows with the same x-value, this will return the + * first one. + * @param {number} xVal The x-value to look for (e.g. millis since epoch). + * @return {?number} The row number, which you can pass to getValue(), or null. + */ +Dygraph.prototype.getRowForX = function(xVal) { + var low = 0, + high = this.numRows() - 1; + + while (low <= high) { + var idx = (high + low) >> 1; + var x = this.getValue(idx, 0); + if (x < xVal) { + low = idx + 1; + } else if (x > xVal) { + high = idx - 1; + } else if (low != idx) { // equal, but there may be an earlier match. + high = idx; + } else { + return idx; + } + } + + return null; +}; + +/** + * Trigger a callback when the dygraph has drawn itself and is ready to be + * manipulated. This is primarily useful when dygraphs has to do an XHR for the + * data (i.e. a URL is passed as the data source) and the chart is drawn + * asynchronously. If the chart has already drawn, the callback will fire + * immediately. + * + * This is a good place to call setAnnotation(). + * + * @param {function(!Dygraph)} callback The callback to trigger when the chart + * is ready. + */ +Dygraph.prototype.ready = function(callback) { + if (this.is_initial_draw_) { + this.readyFns_.push(callback); + } else { + callback.call(this, this); + } +}; + +/** + * Add an event handler. This event handler is kept until the graph is + * destroyed with a call to graph.destroy(). + * + * @param {!Node} elem The element to add the event to. + * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. + * @param {function(Event):(boolean|undefined)} fn The function to call + * on the event. The function takes one parameter: the event object. + * @private + */ +Dygraph.prototype.addAndTrackEvent = function(elem, type, fn) { + utils.addEvent(elem, type, fn); + this.registeredEvents_.push({elem, type, fn}); +}; + +Dygraph.prototype.removeTrackedEvents_ = function() { + if (this.registeredEvents_) { + for (var idx = 0; idx < this.registeredEvents_.length; idx++) { + var reg = this.registeredEvents_[idx]; + utils.removeEvent(reg.elem, reg.type, reg.fn); + } + } + + this.registeredEvents_ = []; +}; + + +// Installed plugins, in order of precedence (most-general to most-specific). +Dygraph.PLUGINS = [ + LegendPlugin, + AxesPlugin, + RangeSelectorPlugin, // Has to be before ChartLabels so that its callbacks are called after ChartLabels' callbacks. + ChartLabelsPlugin, + AnnotationsPlugin, + GridPlugin +]; + +// There are many symbols which have historically been available through the +// Dygraph class. These are exported here for backwards compatibility. +Dygraph.GVizChart = GVizChart; +Dygraph.DASHED_LINE = utils.DASHED_LINE; +Dygraph.DOT_DASH_LINE = utils.DOT_DASH_LINE; +Dygraph.dateAxisLabelFormatter = utils.dateAxisLabelFormatter; +Dygraph.toRGB_ = utils.toRGB_; +Dygraph.findPos = utils.findPos; +Dygraph.pageX = utils.pageX; +Dygraph.pageY = utils.pageY; +Dygraph.dateString_ = utils.dateString_; +Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel; +Dygraph.nonInteractiveModel = Dygraph.nonInteractiveModel_ = DygraphInteraction.nonInteractiveModel_; +Dygraph.Circles = utils.Circles; + +Dygraph.Plugins = { + Legend: LegendPlugin, + Axes: AxesPlugin, + Annotations: AnnotationsPlugin, + ChartLabels: ChartLabelsPlugin, + Grid: GridPlugin, + RangeSelector: RangeSelectorPlugin +}; + +Dygraph.DataHandlers = { + DefaultHandler, + BarsHandler, + CustomBarsHandler, + DefaultFractionHandler, + ErrorBarsHandler, + FractionsBarsHandler +}; + +Dygraph.startPan = DygraphInteraction.startPan; +Dygraph.startZoom = DygraphInteraction.startZoom; +Dygraph.movePan = DygraphInteraction.movePan; +Dygraph.moveZoom = DygraphInteraction.moveZoom; +Dygraph.endPan = DygraphInteraction.endPan; +Dygraph.endZoom = DygraphInteraction.endZoom; + +Dygraph.numericLinearTicks = DygraphTickers.numericLinearTicks; +Dygraph.numericTicks = DygraphTickers.numericTicks; +Dygraph.dateTicker = DygraphTickers.dateTicker; +Dygraph.Granularity = DygraphTickers.Granularity; +Dygraph.getDateAxis = DygraphTickers.getDateAxis; +Dygraph.floatFormat = utils.floatFormat; + +export default Dygraph; diff --git a/debian/missing-sources/dygraph-combined-dd74404.js b/debian/missing-sources/dygraph-combined-dd74404.js deleted file mode 100644 index 8d35eed24..000000000 --- a/debian/missing-sources/dygraph-combined-dd74404.js +++ /dev/null @@ -1,9392 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Dygraph = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { - var prev = originalData[i - rollPeriod]; - if (prev[1] !== null && !isNaN(prev[1])) { - low -= prev[2][0]; - mid -= prev[1]; - high -= prev[2][1]; - count -= 1; - } - } - if (count) { - rollingData[i] = [originalData[i][0], 1.0 * mid / count, [1.0 * low / count, 1.0 * high / count]]; - } else { - rollingData[i] = [originalData[i][0], null, [null, null]]; - } - } - - return rollingData; -}; - -exports['default'] = CustomBarsHandler; -module.exports = exports['default']; - -},{"./bars":4}],2:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler implementation for the error bars option. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - -var _bars = require('./bars'); - -var _bars2 = _interopRequireDefault(_bars); - -/** - * @constructor - * @extends BarsHandler - */ -var ErrorBarsHandler = function ErrorBarsHandler() {}; - -ErrorBarsHandler.prototype = new _bars2["default"](); - -/** @inheritDoc */ -ErrorBarsHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var x, y, variance, point; - var sigma = options.get("sigma"); - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - x = rawData[j][0]; - point = rawData[j][i]; - if (logScale && point !== null) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point[0] <= 0 || point[0] - sigma * point[1] <= 0) { - point = null; - } - } - // Extract to the unified data format. - if (point !== null) { - y = point[0]; - if (y !== null && !isNaN(y)) { - variance = sigma * point[1]; - // preserve original error value in extras for further - // filtering - series.push([x, y, [y - variance, y + variance, point[1]]]); - } else { - series.push([x, y, [y, y, y]]); - } - } else { - series.push([x, null, [null, null, null]]); - } - } - return series; -}; - -/** @inheritDoc */ -ErrorBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - var sigma = options.get("sigma"); - - var i, j, y, v, sum, num_ok, stddev, variance, value; - - // Calculate the rolling average for the first rollPeriod - 1 points - // where there is not enough data to roll over the full number of points - for (i = 0; i < originalData.length; i++) { - sum = 0; - variance = 0; - num_ok = 0; - for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) { - y = originalData[j][1]; - if (y === null || isNaN(y)) continue; - num_ok++; - sum += y; - variance += Math.pow(originalData[j][2][2], 2); - } - if (num_ok) { - stddev = Math.sqrt(variance) / num_ok; - value = sum / num_ok; - rollingData[i] = [originalData[i][0], value, [value - sigma * stddev, value + sigma * stddev]]; - } else { - // This explicitly preserves NaNs to aid with "independent - // series". - // See testRollingAveragePreservesNaNs. - v = rollPeriod == 1 ? originalData[i][1] : null; - rollingData[i] = [originalData[i][0], v, [v, v]]; - } - } - - return rollingData; -}; - -exports["default"] = ErrorBarsHandler; -module.exports = exports["default"]; - -},{"./bars":4}],3:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler implementation for the combination - * of error bars and fractions options. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - -var _bars = require('./bars'); - -var _bars2 = _interopRequireDefault(_bars); - -/** - * @constructor - * @extends Dygraph.DataHandlers.BarsHandler - */ -var FractionsBarsHandler = function FractionsBarsHandler() {}; - -FractionsBarsHandler.prototype = new _bars2["default"](); - -/** @inheritDoc */ -FractionsBarsHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var x, y, point, num, den, value, stddev, variance; - var mult = 100.0; - var sigma = options.get("sigma"); - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - x = rawData[j][0]; - point = rawData[j][i]; - if (logScale && point !== null) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point[0] <= 0 || point[1] <= 0) { - point = null; - } - } - // Extract to the unified data format. - if (point !== null) { - num = point[0]; - den = point[1]; - if (num !== null && !isNaN(num)) { - value = den ? num / den : 0.0; - stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0; - variance = mult * stddev; - y = mult * value; - // preserve original values in extras for further filtering - series.push([x, y, [y - variance, y + variance, num, den]]); - } else { - series.push([x, num, [num, num, num, den]]); - } - } else { - series.push([x, null, [null, null, null, null]]); - } - } - return series; -}; - -/** @inheritDoc */ -FractionsBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - var sigma = options.get("sigma"); - var wilsonInterval = options.get("wilsonInterval"); - - var low, high, i, stddev; - var num = 0; - var den = 0; // numerator/denominator - var mult = 100.0; - for (i = 0; i < originalData.length; i++) { - num += originalData[i][2][2]; - den += originalData[i][2][3]; - if (i - rollPeriod >= 0) { - num -= originalData[i - rollPeriod][2][2]; - den -= originalData[i - rollPeriod][2][3]; - } - - var date = originalData[i][0]; - var value = den ? num / den : 0.0; - if (wilsonInterval) { - // For more details on this confidence interval, see: - // http://en.wikipedia.org/wiki/Binomial_confidence_interval - if (den) { - var p = value < 0 ? 0 : value, - n = den; - var pm = sigma * Math.sqrt(p * (1 - p) / n + sigma * sigma / (4 * n * n)); - var denom = 1 + sigma * sigma / den; - low = (p + sigma * sigma / (2 * den) - pm) / denom; - high = (p + sigma * sigma / (2 * den) + pm) / denom; - rollingData[i] = [date, p * mult, [low * mult, high * mult]]; - } else { - rollingData[i] = [date, 0, [0, 0]]; - } - } else { - stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0; - rollingData[i] = [date, mult * value, [mult * (value - stddev), mult * (value + stddev)]]; - } - } - - return rollingData; -}; - -exports["default"] = FractionsBarsHandler; -module.exports = exports["default"]; - -},{"./bars":4}],4:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler base implementation for the "bar" - * data formats. This implementation must be extended and the - * extractSeries and rollingAverage must be implemented. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -/*global DygraphLayout:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _datahandler = require('./datahandler'); - -var _datahandler2 = _interopRequireDefault(_datahandler); - -var _dygraphLayout = require('../dygraph-layout'); - -var _dygraphLayout2 = _interopRequireDefault(_dygraphLayout); - -/** - * @constructor - * @extends {Dygraph.DataHandler} - */ -var BarsHandler = function BarsHandler() { - _datahandler2['default'].call(this); -}; -BarsHandler.prototype = new _datahandler2['default'](); - -// TODO(danvk): figure out why the jsdoc has to be copy/pasted from superclass. -// (I get closure compiler errors if this isn't here.) -/** - * @override - * @param {!Array.} rawData The raw data passed into dygraphs where - * rawData[i] = [x,ySeries1,...,ySeriesN]. - * @param {!number} seriesIndex Index of the series to extract. All other - * series should be ignored. - * @param {!DygraphOptions} options Dygraph options. - * @return {Array.<[!number,?number,?]>} The series in the unified data format - * where series[i] = [x,y,{extras}]. - */ -BarsHandler.prototype.extractSeries = function (rawData, seriesIndex, options) { - // Not implemented here must be extended -}; - -/** - * @override - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!number} rollPeriod The number of points over which to average the data - * @param {!DygraphOptions} options The dygraph options. - * TODO(danvk): be more specific than "Array" here. - * @return {!Array.<[!number,?number,?]>} the rolled series. - */ -BarsHandler.prototype.rollingAverage = function (series, rollPeriod, options) { - // Not implemented here, must be extended. -}; - -/** @inheritDoc */ -BarsHandler.prototype.onPointsCreated_ = function (series, points) { - for (var i = 0; i < series.length; ++i) { - var item = series[i]; - var point = points[i]; - point.y_top = NaN; - point.y_bottom = NaN; - point.yval_minus = _datahandler2['default'].parseFloat(item[2][0]); - point.yval_plus = _datahandler2['default'].parseFloat(item[2][1]); - } -}; - -/** @inheritDoc */ -BarsHandler.prototype.getExtremeYValues = function (series, dateWindow, options) { - var minY = null, - maxY = null, - y; - - var firstIdx = 0; - var lastIdx = series.length - 1; - - for (var j = firstIdx; j <= lastIdx; j++) { - y = series[j][1]; - if (y === null || isNaN(y)) continue; - - var low = series[j][2][0]; - var high = series[j][2][1]; - - if (low > y) low = y; // this can happen with custom bars, - if (high < y) high = y; // e.g. in tests/custom-bars.html - - if (maxY === null || high > maxY) maxY = high; - if (minY === null || low < minY) minY = low; - } - - return [minY, maxY]; -}; - -/** @inheritDoc */ -BarsHandler.prototype.onLineEvaluated = function (points, axis, logscale) { - var point; - for (var j = 0; j < points.length; j++) { - // Copy over the error terms - point = points[j]; - point.y_top = _dygraphLayout2['default'].calcYNormal_(axis, point.yval_minus, logscale); - point.y_bottom = _dygraphLayout2['default'].calcYNormal_(axis, point.yval_plus, logscale); - } -}; - -exports['default'] = BarsHandler; -module.exports = exports['default']; - -},{"../dygraph-layout":12,"./datahandler":5}],5:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview This file contains the managment of data handlers - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - * - * The idea is to define a common, generic data format that works for all data - * structures supported by dygraphs. To make this possible, the DataHandler - * interface is introduced. This makes it possible, that dygraph itself can work - * with the same logic for every data type independent of the actual format and - * the DataHandler takes care of the data format specific jobs. - * DataHandlers are implemented for all data types supported by Dygraphs and - * return Dygraphs compliant formats. - * By default the correct DataHandler is chosen based on the options set. - * Optionally the user may use his own DataHandler (similar to the plugin - * system). - * - * - * The unified data format returend by each handler is defined as so: - * series[n][point] = [x,y,(extras)] - * - * This format contains the common basis that is needed to draw a simple line - * series extended by optional extras for more complex graphing types. It - * contains a primitive x value as first array entry, a primitive y value as - * second array entry and an optional extras object for additional data needed. - * - * x must always be a number. - * y must always be a number, NaN of type number or null. - * extras is optional and must be interpreted by the DataHandler. It may be of - * any type. - * - * In practice this might look something like this: - * default: [x, yVal] - * errorBar / customBar: [x, yVal, [yTopVariance, yBottomVariance] ] - * - */ -/*global Dygraph:false */ -/*global DygraphLayout:false */ - -"use strict"; - -/** - * - * The data handler is responsible for all data specific operations. All of the - * series data it receives and returns is always in the unified data format. - * Initially the unified data is created by the extractSeries method - * @constructor - */ -Object.defineProperty(exports, "__esModule", { - value: true -}); -var DygraphDataHandler = function DygraphDataHandler() {}; - -var handler = DygraphDataHandler; - -/** - * X-value array index constant for unified data samples. - * @const - * @type {number} - */ -handler.X = 0; - -/** - * Y-value array index constant for unified data samples. - * @const - * @type {number} - */ -handler.Y = 1; - -/** - * Extras-value array index constant for unified data samples. - * @const - * @type {number} - */ -handler.EXTRAS = 2; - -/** - * Extracts one series from the raw data (a 2D array) into an array of the - * unified data format. - * This is where undesirable points (i.e. negative values on log scales and - * missing values through which we wish to connect lines) are dropped. - * TODO(danvk): the "missing values" bit above doesn't seem right. - * - * @param {!Array.} rawData The raw data passed into dygraphs where - * rawData[i] = [x,ySeries1,...,ySeriesN]. - * @param {!number} seriesIndex Index of the series to extract. All other - * series should be ignored. - * @param {!DygraphOptions} options Dygraph options. - * @return {Array.<[!number,?number,?]>} The series in the unified data format - * where series[i] = [x,y,{extras}]. - */ -handler.prototype.extractSeries = function (rawData, seriesIndex, options) {}; - -/** - * Converts a series to a Point array. The resulting point array must be - * returned in increasing order of idx property. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!string} setName Name of the series. - * @param {!number} boundaryIdStart Index offset of the first point, equal to the - * number of skipped points left of the date window minimum (if any). - * @return {!Array.} List of points for this series. - */ -handler.prototype.seriesToPoints = function (series, setName, boundaryIdStart) { - // TODO(bhs): these loops are a hot-spot for high-point-count charts. In - // fact, - // on chrome+linux, they are 6 times more expensive than iterating through - // the - // points and drawing the lines. The brunt of the cost comes from allocating - // the |point| structures. - var points = []; - for (var i = 0; i < series.length; ++i) { - var item = series[i]; - var yraw = item[1]; - var yval = yraw === null ? null : handler.parseFloat(yraw); - var point = { - x: NaN, - y: NaN, - xval: handler.parseFloat(item[0]), - yval: yval, - name: setName, // TODO(danvk): is this really necessary? - idx: i + boundaryIdStart - }; - points.push(point); - } - this.onPointsCreated_(series, points); - return points; -}; - -/** - * Callback called for each series after the series points have been generated - * which will later be used by the plotters to draw the graph. - * Here data may be added to the seriesPoints which is needed by the plotters. - * The indexes of series and points are in sync meaning the original data - * sample for series[i] is points[i]. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!Array.} points The corresponding points passed - * to the plotter. - * @protected - */ -handler.prototype.onPointsCreated_ = function (series, points) {}; - -/** - * Calculates the rolling average of a data set. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!number} rollPeriod The number of points over which to average the data - * @param {!DygraphOptions} options The dygraph options. - * @return {!Array.<[!number,?number,?]>} the rolled series. - */ -handler.prototype.rollingAverage = function (series, rollPeriod, options) {}; - -/** - * Computes the range of the data series (including confidence intervals). - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x, y, {extras}]. - * @param {!Array.} dateWindow The x-value range to display with - * the format: [min, max]. - * @param {!DygraphOptions} options The dygraph options. - * @return {Array.} The low and high extremes of the series in the - * given window with the format: [low, high]. - */ -handler.prototype.getExtremeYValues = function (series, dateWindow, options) {}; - -/** - * Callback called for each series after the layouting data has been - * calculated before the series is drawn. Here normalized positioning data - * should be calculated for the extras of each point. - * - * @param {!Array.} points The points passed to - * the plotter. - * @param {!Object} axis The axis on which the series will be plotted. - * @param {!boolean} logscale Weather or not to use a logscale. - */ -handler.prototype.onLineEvaluated = function (points, axis, logscale) {}; - -/** - * Helper method that computes the y value of a line defined by the points p1 - * and p2 and a given x value. - * - * @param {!Array.} p1 left point ([x,y]). - * @param {!Array.} p2 right point ([x,y]). - * @param {!number} xValue The x value to compute the y-intersection for. - * @return {number} corresponding y value to x on the line defined by p1 and p2. - * @private - */ -handler.prototype.computeYInterpolation_ = function (p1, p2, xValue) { - var deltaY = p2[1] - p1[1]; - var deltaX = p2[0] - p1[0]; - var gradient = deltaY / deltaX; - var growth = (xValue - p1[0]) * gradient; - return p1[1] + growth; -}; - -/** - * Helper method that returns the first and the last index of the given series - * that lie inside the given dateWindow. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!Array.} dateWindow The x-value range to display with - * the format: [min,max]. - * @return {!Array.<[!number,?number,?]>} The samples of the series that - * are in the given date window. - * @private - */ -handler.prototype.getIndexesInWindow_ = function (series, dateWindow) { - var firstIdx = 0, - lastIdx = series.length - 1; - if (dateWindow) { - var idx = 0; - var low = dateWindow[0]; - var high = dateWindow[1]; - - // Start from each side of the array to minimize the performance - // needed. - while (idx < series.length - 1 && series[idx][0] < low) { - firstIdx++; - idx++; - } - idx = series.length - 1; - while (idx > 0 && series[idx][0] > high) { - lastIdx--; - idx--; - } - } - if (firstIdx <= lastIdx) { - return [firstIdx, lastIdx]; - } else { - return [0, series.length - 1]; - } -}; - -/** - * Optimized replacement for parseFloat, which was way too slow when almost - * all values were type number, with few edge cases, none of which were strings. - * @param {?number} val - * @return {number} - * @protected - */ -handler.parseFloat = function (val) { - // parseFloat(null) is NaN - if (val === null) { - return NaN; - } - - // Assume it's a number or NaN. If it's something else, I'll be shocked. - return val; -}; - -exports["default"] = DygraphDataHandler; -module.exports = exports["default"]; - -},{}],6:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler implementation for the fractions option. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _datahandler = require('./datahandler'); - -var _datahandler2 = _interopRequireDefault(_datahandler); - -var _default = require('./default'); - -var _default2 = _interopRequireDefault(_default); - -/** - * @extends DefaultHandler - * @constructor - */ -var DefaultFractionHandler = function DefaultFractionHandler() {}; - -DefaultFractionHandler.prototype = new _default2['default'](); - -DefaultFractionHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var x, y, point, num, den, value; - var mult = 100.0; - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - x = rawData[j][0]; - point = rawData[j][i]; - if (logScale && point !== null) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point[0] <= 0 || point[1] <= 0) { - point = null; - } - } - // Extract to the unified data format. - if (point !== null) { - num = point[0]; - den = point[1]; - if (num !== null && !isNaN(num)) { - value = den ? num / den : 0.0; - y = mult * value; - // preserve original values in extras for further filtering - series.push([x, y, [num, den]]); - } else { - series.push([x, num, [num, den]]); - } - } else { - series.push([x, null, [null, null]]); - } - } - return series; -}; - -DefaultFractionHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - - var i; - var num = 0; - var den = 0; // numerator/denominator - var mult = 100.0; - for (i = 0; i < originalData.length; i++) { - num += originalData[i][2][0]; - den += originalData[i][2][1]; - if (i - rollPeriod >= 0) { - num -= originalData[i - rollPeriod][2][0]; - den -= originalData[i - rollPeriod][2][1]; - } - - var date = originalData[i][0]; - var value = den ? num / den : 0.0; - rollingData[i] = [date, mult * value]; - } - - return rollingData; -}; - -exports['default'] = DefaultFractionHandler; -module.exports = exports['default']; - -},{"./datahandler":5,"./default":7}],7:[function(require,module,exports){ -/** - * @license - * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DataHandler default implementation used for simple line charts. - * @author David Eberlein (david.eberlein@ch.sauter-bc.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _datahandler = require('./datahandler'); - -var _datahandler2 = _interopRequireDefault(_datahandler); - -/** - * @constructor - * @extends Dygraph.DataHandler - */ -var DefaultHandler = function DefaultHandler() {}; - -DefaultHandler.prototype = new _datahandler2['default'](); - -/** @inheritDoc */ -DefaultHandler.prototype.extractSeries = function (rawData, i, options) { - // TODO(danvk): pre-allocate series here. - var series = []; - var logScale = options.get('logscale'); - for (var j = 0; j < rawData.length; j++) { - var x = rawData[j][0]; - var point = rawData[j][i]; - if (logScale) { - // On the log scale, points less than zero do not exist. - // This will create a gap in the chart. - if (point <= 0) { - point = null; - } - } - series.push([x, point]); - } - return series; -}; - -/** @inheritDoc */ -DefaultHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { - rollPeriod = Math.min(rollPeriod, originalData.length); - var rollingData = []; - - var i, j, y, sum, num_ok; - // Calculate the rolling average for the first rollPeriod - 1 points - // where - // there is not enough data to roll over the full number of points - if (rollPeriod == 1) { - return originalData; - } - for (i = 0; i < originalData.length; i++) { - sum = 0; - num_ok = 0; - for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) { - y = originalData[j][1]; - if (y === null || isNaN(y)) continue; - num_ok++; - sum += originalData[j][1]; - } - if (num_ok) { - rollingData[i] = [originalData[i][0], sum / num_ok]; - } else { - rollingData[i] = [originalData[i][0], null]; - } - } - - return rollingData; -}; - -/** @inheritDoc */ -DefaultHandler.prototype.getExtremeYValues = function (series, dateWindow, options) { - var minY = null, - maxY = null, - y; - var firstIdx = 0, - lastIdx = series.length - 1; - - for (var j = firstIdx; j <= lastIdx; j++) { - y = series[j][1]; - if (y === null || isNaN(y)) continue; - if (maxY === null || y > maxY) { - maxY = y; - } - if (minY === null || y < minY) { - minY = y; - } - } - return [minY, maxY]; -}; - -exports['default'] = DefaultHandler; -module.exports = exports['default']; - -},{"./datahandler":5}],8:[function(require,module,exports){ -/** - * @license - * Copyright 2006 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview Based on PlotKit.CanvasRenderer, but modified to meet the - * needs of dygraphs. - * - * In particular, support for: - * - grid overlays - * - error bars - * - dygraphs attribute system - */ - -/** - * The DygraphCanvasRenderer class does the actual rendering of the chart onto - * a canvas. It's based on PlotKit.CanvasRenderer. - * @param {Object} element The canvas to attach to - * @param {Object} elementContext The 2d context of the canvas (injected so it - * can be mocked for testing.) - * @param {Layout} layout The DygraphLayout object for this graph. - * @constructor - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -var _dygraph = require('./dygraph'); - -var _dygraph2 = _interopRequireDefault(_dygraph); - -/** - * @constructor - * - * This gets called when there are "new points" to chart. This is generally the - * case when the underlying data being charted has changed. It is _not_ called - * in the common case that the user has zoomed or is panning the view. - * - * The chart canvas has already been created by the Dygraph object. The - * renderer simply gets a drawing context. - * - * @param {Dygraph} dygraph The chart to which this renderer belongs. - * @param {HTMLCanvasElement} element The <canvas> DOM element on which to draw. - * @param {CanvasRenderingContext2D} elementContext The drawing context. - * @param {DygraphLayout} layout The chart's DygraphLayout object. - * - * TODO(danvk): remove the elementContext property. - */ -var DygraphCanvasRenderer = function DygraphCanvasRenderer(dygraph, element, elementContext, layout) { - this.dygraph_ = dygraph; - - this.layout = layout; - this.element = element; - this.elementContext = elementContext; - - this.height = dygraph.height_; - this.width = dygraph.width_; - - // --- check whether everything is ok before we return - if (!utils.isCanvasSupported(this.element)) { - throw "Canvas is not supported."; - } - - // internal state - this.area = layout.getPlotArea(); - - // Set up a clipping area for the canvas (and the interaction canvas). - // This ensures that we don't overdraw. - // on Android 3 and 4, setting a clipping area on a canvas prevents it from - // displaying anything. - if (!utils.isAndroid()) { - var ctx = this.dygraph_.canvas_ctx_; - ctx.beginPath(); - ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); - ctx.clip(); - - ctx = this.dygraph_.hidden_ctx_; - ctx.beginPath(); - ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); - ctx.clip(); - } -}; - -/** - * Clears out all chart content and DOM elements. - * This is called immediately before render() on every frame, including - * during zooms and pans. - * @private - */ -DygraphCanvasRenderer.prototype.clear = function () { - this.elementContext.clearRect(0, 0, this.width, this.height); -}; - -/** - * This method is responsible for drawing everything on the chart, including - * lines, error bars, fills and axes. - * It is called immediately after clear() on every frame, including during pans - * and zooms. - * @private - */ -DygraphCanvasRenderer.prototype.render = function () { - // attaches point.canvas{x,y} - this._updatePoints(); - - // actually draws the chart. - this._renderLineChart(); -}; - -/** - * Returns a predicate to be used with an iterator, which will - * iterate over points appropriately, depending on whether - * connectSeparatedPoints is true. When it's false, the predicate will - * skip over points with missing yVals. - */ -DygraphCanvasRenderer._getIteratorPredicate = function (connectSeparatedPoints) { - return connectSeparatedPoints ? DygraphCanvasRenderer._predicateThatSkipsEmptyPoints : null; -}; - -DygraphCanvasRenderer._predicateThatSkipsEmptyPoints = function (array, idx) { - return array[idx].yval !== null; -}; - -/** - * Draws a line with the styles passed in and calls all the drawPointCallbacks. - * @param {Object} e The dictionary passed to the plotter function. - * @private - */ -DygraphCanvasRenderer._drawStyledLine = function (e, color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize) { - var g = e.dygraph; - // TODO(konigsberg): Compute attributes outside this method call. - var stepPlot = g.getBooleanOption("stepPlot", e.setName); - - if (!utils.isArrayLike(strokePattern)) { - strokePattern = null; - } - - var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName); - - var points = e.points; - var setName = e.setName; - var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); - - var stroking = strokePattern && strokePattern.length >= 2; - - var ctx = e.drawingContext; - ctx.save(); - if (stroking) { - if (ctx.setLineDash) ctx.setLineDash(strokePattern); - } - - var pointsOnLine = DygraphCanvasRenderer._drawSeries(e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color); - DygraphCanvasRenderer._drawPointsOnLine(e, pointsOnLine, drawPointCallback, color, pointSize); - - if (stroking) { - if (ctx.setLineDash) ctx.setLineDash([]); - } - - ctx.restore(); -}; - -/** - * This does the actual drawing of lines on the canvas, for just one series. - * Returns a list of [canvasx, canvasy] pairs for points for which a - * drawPointCallback should be fired. These include isolated points, or all - * points if drawPoints=true. - * @param {Object} e The dictionary passed to the plotter function. - * @private - */ -DygraphCanvasRenderer._drawSeries = function (e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) { - - var prevCanvasX = null; - var prevCanvasY = null; - var nextCanvasY = null; - var isIsolated; // true if this point is isolated (no line segments) - var point; // the point being processed in the while loop - var pointsOnLine = []; // Array of [canvasx, canvasy] pairs. - var first = true; // the first cycle through the while loop - - var ctx = e.drawingContext; - ctx.beginPath(); - ctx.strokeStyle = color; - ctx.lineWidth = strokeWidth; - - // NOTE: we break the iterator's encapsulation here for about a 25% speedup. - var arr = iter.array_; - var limit = iter.end_; - var predicate = iter.predicate_; - - for (var i = iter.start_; i < limit; i++) { - point = arr[i]; - if (predicate) { - while (i < limit && !predicate(arr, i)) { - i++; - } - if (i == limit) break; - point = arr[i]; - } - - // FIXME: The 'canvasy != canvasy' test here catches NaN values but the test - // doesn't catch Infinity values. Could change this to - // !isFinite(point.canvasy), but I assume it avoids isNaN for performance? - if (point.canvasy === null || point.canvasy != point.canvasy) { - if (stepPlot && prevCanvasX !== null) { - // Draw a horizontal line to the start of the missing data - ctx.moveTo(prevCanvasX, prevCanvasY); - ctx.lineTo(point.canvasx, prevCanvasY); - } - prevCanvasX = prevCanvasY = null; - } else { - isIsolated = false; - if (drawGapPoints || !prevCanvasX) { - iter.nextIdx_ = i; - iter.next(); - nextCanvasY = iter.hasNext ? iter.peek.canvasy : null; - - var isNextCanvasYNullOrNaN = nextCanvasY === null || nextCanvasY != nextCanvasY; - isIsolated = !prevCanvasX && isNextCanvasYNullOrNaN; - if (drawGapPoints) { - // Also consider a point to be "isolated" if it's adjacent to a - // null point, excluding the graph edges. - if (!first && !prevCanvasX || iter.hasNext && isNextCanvasYNullOrNaN) { - isIsolated = true; - } - } - } - - if (prevCanvasX !== null) { - if (strokeWidth) { - if (stepPlot) { - ctx.moveTo(prevCanvasX, prevCanvasY); - ctx.lineTo(point.canvasx, prevCanvasY); - } - - ctx.lineTo(point.canvasx, point.canvasy); - } - } else { - ctx.moveTo(point.canvasx, point.canvasy); - } - if (drawPoints || isIsolated) { - pointsOnLine.push([point.canvasx, point.canvasy, point.idx]); - } - prevCanvasX = point.canvasx; - prevCanvasY = point.canvasy; - } - first = false; - } - ctx.stroke(); - return pointsOnLine; -}; - -/** - * This fires the drawPointCallback functions, which draw dots on the points by - * default. This gets used when the "drawPoints" option is set, or when there - * are isolated points. - * @param {Object} e The dictionary passed to the plotter function. - * @private - */ -DygraphCanvasRenderer._drawPointsOnLine = function (e, pointsOnLine, drawPointCallback, color, pointSize) { - var ctx = e.drawingContext; - for (var idx = 0; idx < pointsOnLine.length; idx++) { - var cb = pointsOnLine[idx]; - ctx.save(); - drawPointCallback.call(e.dygraph, e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]); - ctx.restore(); - } -}; - -/** - * Attaches canvas coordinates to the points array. - * @private - */ -DygraphCanvasRenderer.prototype._updatePoints = function () { - // Update Points - // TODO(danvk): here - // - // TODO(bhs): this loop is a hot-spot for high-point-count charts. These - // transformations can be pushed into the canvas via linear transformation - // matrices. - // NOTE(danvk): this is trickier than it sounds at first. The transformation - // needs to be done before the .moveTo() and .lineTo() calls, but must be - // undone before the .stroke() call to ensure that the stroke width is - // unaffected. An alternative is to reduce the stroke width in the - // transformed coordinate space, but you can't specify different values for - // each dimension (as you can with .scale()). The speedup here is ~12%. - var sets = this.layout.points; - for (var i = sets.length; i--;) { - var points = sets[i]; - for (var j = points.length; j--;) { - var point = points[j]; - point.canvasx = this.area.w * point.x + this.area.x; - point.canvasy = this.area.h * point.y + this.area.y; - } - } -}; - -/** - * Add canvas Actually draw the lines chart, including error bars. - * - * This function can only be called if DygraphLayout's points array has been - * updated with canvas{x,y} attributes, i.e. by - * DygraphCanvasRenderer._updatePoints. - * - * @param {string=} opt_seriesName when specified, only that series will - * be drawn. (This is used for expedited redrawing with highlightSeriesOpts) - * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing - * context. However, lines are typically drawn on the object's - * elementContext. - * @private - */ -DygraphCanvasRenderer.prototype._renderLineChart = function (opt_seriesName, opt_ctx) { - var ctx = opt_ctx || this.elementContext; - var i; - - var sets = this.layout.points; - var setNames = this.layout.setNames; - var setName; - - this.colors = this.dygraph_.colorsMap_; - - // Determine which series have specialized plotters. - var plotter_attr = this.dygraph_.getOption("plotter"); - var plotters = plotter_attr; - if (!utils.isArrayLike(plotters)) { - plotters = [plotters]; - } - - var setPlotters = {}; // series name -> plotter fn. - for (i = 0; i < setNames.length; i++) { - setName = setNames[i]; - var setPlotter = this.dygraph_.getOption("plotter", setName); - if (setPlotter == plotter_attr) continue; // not specialized. - - setPlotters[setName] = setPlotter; - } - - for (i = 0; i < plotters.length; i++) { - var plotter = plotters[i]; - var is_last = i == plotters.length - 1; - - for (var j = 0; j < sets.length; j++) { - setName = setNames[j]; - if (opt_seriesName && setName != opt_seriesName) continue; - - var points = sets[j]; - - // Only throw in the specialized plotters on the last iteration. - var p = plotter; - if (setName in setPlotters) { - if (is_last) { - p = setPlotters[setName]; - } else { - // Don't use the standard plotters in this case. - continue; - } - } - - var color = this.colors[setName]; - var strokeWidth = this.dygraph_.getOption("strokeWidth", setName); - - ctx.save(); - ctx.strokeStyle = color; - ctx.lineWidth = strokeWidth; - p({ - points: points, - setName: setName, - drawingContext: ctx, - color: color, - strokeWidth: strokeWidth, - dygraph: this.dygraph_, - axis: this.dygraph_.axisPropertiesForSeries(setName), - plotArea: this.area, - seriesIndex: j, - seriesCount: sets.length, - singleSeriesName: opt_seriesName, - allSeriesPoints: sets - }); - ctx.restore(); - } - } -}; - -/** - * Standard plotters. These may be used by clients via Dygraph.Plotters. - * See comments there for more details. - */ -DygraphCanvasRenderer._Plotters = { - linePlotter: function linePlotter(e) { - DygraphCanvasRenderer._linePlotter(e); - }, - - fillPlotter: function fillPlotter(e) { - DygraphCanvasRenderer._fillPlotter(e); - }, - - errorPlotter: function errorPlotter(e) { - DygraphCanvasRenderer._errorPlotter(e); - } -}; - -/** - * Plotter which draws the central lines for a series. - * @private - */ -DygraphCanvasRenderer._linePlotter = function (e) { - var g = e.dygraph; - var setName = e.setName; - var strokeWidth = e.strokeWidth; - - // TODO(danvk): Check if there's any performance impact of just calling - // getOption() inside of _drawStyledLine. Passing in so many parameters makes - // this code a bit nasty. - var borderWidth = g.getNumericOption("strokeBorderWidth", setName); - var drawPointCallback = g.getOption("drawPointCallback", setName) || utils.Circles.DEFAULT; - var strokePattern = g.getOption("strokePattern", setName); - var drawPoints = g.getBooleanOption("drawPoints", setName); - var pointSize = g.getNumericOption("pointSize", setName); - - if (borderWidth && strokeWidth) { - DygraphCanvasRenderer._drawStyledLine(e, g.getOption("strokeBorderColor", setName), strokeWidth + 2 * borderWidth, strokePattern, drawPoints, drawPointCallback, pointSize); - } - - DygraphCanvasRenderer._drawStyledLine(e, e.color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize); -}; - -/** - * Draws the shaded error bars/confidence intervals for each series. - * This happens before the center lines are drawn, since the center lines - * need to be drawn on top of the error bars for all series. - * @private - */ -DygraphCanvasRenderer._errorPlotter = function (e) { - var g = e.dygraph; - var setName = e.setName; - var errorBars = g.getBooleanOption("errorBars") || g.getBooleanOption("customBars"); - if (!errorBars) return; - - var fillGraph = g.getBooleanOption("fillGraph", setName); - if (fillGraph) { - console.warn("Can't use fillGraph option with error bars"); - } - - var ctx = e.drawingContext; - var color = e.color; - var fillAlpha = g.getNumericOption('fillAlpha', setName); - var stepPlot = g.getBooleanOption("stepPlot", setName); - var points = e.points; - - var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); - - var newYs; - - // setup graphics context - var prevX = NaN; - var prevY = NaN; - var prevYs = [-1, -1]; - // should be same color as the lines but only 15% opaque. - var rgb = utils.toRGB_(color); - var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; - ctx.fillStyle = err_color; - ctx.beginPath(); - - var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(x) { - return x === null || x === undefined || isNaN(x); - }; - - while (iter.hasNext) { - var point = iter.next(); - if (!stepPlot && isNullUndefinedOrNaN(point.y) || stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY)) { - prevX = NaN; - continue; - } - - newYs = [point.y_bottom, point.y_top]; - if (stepPlot) { - prevY = point.y; - } - - // The documentation specifically disallows nulls inside the point arrays, - // but in case it happens we should do something sensible. - if (isNaN(newYs[0])) newYs[0] = point.y; - if (isNaN(newYs[1])) newYs[1] = point.y; - - newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y; - newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y; - if (!isNaN(prevX)) { - if (stepPlot) { - ctx.moveTo(prevX, prevYs[0]); - ctx.lineTo(point.canvasx, prevYs[0]); - ctx.lineTo(point.canvasx, prevYs[1]); - } else { - ctx.moveTo(prevX, prevYs[0]); - ctx.lineTo(point.canvasx, newYs[0]); - ctx.lineTo(point.canvasx, newYs[1]); - } - ctx.lineTo(prevX, prevYs[1]); - ctx.closePath(); - } - prevYs = newYs; - prevX = point.canvasx; - } - ctx.fill(); -}; - -/** - * Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which are - * superfluous. It accumulates all movements which haven't changed the x-value - * and only applies the two with the most extreme y-values. - * - * Calls to lineTo/moveTo must have non-decreasing x-values. - */ -DygraphCanvasRenderer._fastCanvasProxy = function (context) { - var pendingActions = []; // array of [type, x, y] tuples - var lastRoundedX = null; - var lastFlushedX = null; - - var LINE_TO = 1, - MOVE_TO = 2; - - var actionCount = 0; // number of moveTos and lineTos passed to context. - - // Drop superfluous motions - // Assumes all pendingActions have the same (rounded) x-value. - var compressActions = function compressActions(opt_losslessOnly) { - if (pendingActions.length <= 1) return; - - // Lossless compression: drop inconsequential moveTos. - for (var i = pendingActions.length - 1; i > 0; i--) { - var action = pendingActions[i]; - if (action[0] == MOVE_TO) { - var prevAction = pendingActions[i - 1]; - if (prevAction[1] == action[1] && prevAction[2] == action[2]) { - pendingActions.splice(i, 1); - } - } - } - - // Lossless compression: ... drop consecutive moveTos ... - for (var i = 0; i < pendingActions.length - 1;) /* incremented internally */{ - var action = pendingActions[i]; - if (action[0] == MOVE_TO && pendingActions[i + 1][0] == MOVE_TO) { - pendingActions.splice(i, 1); - } else { - i++; - } - } - - // Lossy compression: ... drop all but the extreme y-values ... - if (pendingActions.length > 2 && !opt_losslessOnly) { - // keep an initial moveTo, but drop all others. - var startIdx = 0; - if (pendingActions[0][0] == MOVE_TO) startIdx++; - var minIdx = null, - maxIdx = null; - for (var i = startIdx; i < pendingActions.length; i++) { - var action = pendingActions[i]; - if (action[0] != LINE_TO) continue; - if (minIdx === null && maxIdx === null) { - minIdx = i; - maxIdx = i; - } else { - var y = action[2]; - if (y < pendingActions[minIdx][2]) { - minIdx = i; - } else if (y > pendingActions[maxIdx][2]) { - maxIdx = i; - } - } - } - var minAction = pendingActions[minIdx], - maxAction = pendingActions[maxIdx]; - pendingActions.splice(startIdx, pendingActions.length - startIdx); - if (minIdx < maxIdx) { - pendingActions.push(minAction); - pendingActions.push(maxAction); - } else if (minIdx > maxIdx) { - pendingActions.push(maxAction); - pendingActions.push(minAction); - } else { - pendingActions.push(minAction); - } - } - }; - - var flushActions = function flushActions(opt_noLossyCompression) { - compressActions(opt_noLossyCompression); - for (var i = 0, len = pendingActions.length; i < len; i++) { - var action = pendingActions[i]; - if (action[0] == LINE_TO) { - context.lineTo(action[1], action[2]); - } else if (action[0] == MOVE_TO) { - context.moveTo(action[1], action[2]); - } - } - if (pendingActions.length) { - lastFlushedX = pendingActions[pendingActions.length - 1][1]; - } - actionCount += pendingActions.length; - pendingActions = []; - }; - - var addAction = function addAction(action, x, y) { - var rx = Math.round(x); - if (lastRoundedX === null || rx != lastRoundedX) { - // if there are large gaps on the x-axis, it's essential to keep the - // first and last point as well. - var hasGapOnLeft = lastRoundedX - lastFlushedX > 1, - hasGapOnRight = rx - lastRoundedX > 1, - hasGap = hasGapOnLeft || hasGapOnRight; - flushActions(hasGap); - lastRoundedX = rx; - } - pendingActions.push([action, x, y]); - }; - - return { - moveTo: function moveTo(x, y) { - addAction(MOVE_TO, x, y); - }, - lineTo: function lineTo(x, y) { - addAction(LINE_TO, x, y); - }, - - // for major operations like stroke/fill, we skip compression to ensure - // that there are no artifacts at the right edge. - stroke: function stroke() { - flushActions(true);context.stroke(); - }, - fill: function fill() { - flushActions(true);context.fill(); - }, - beginPath: function beginPath() { - flushActions(true);context.beginPath(); - }, - closePath: function closePath() { - flushActions(true);context.closePath(); - }, - - _count: function _count() { - return actionCount; - } - }; -}; - -/** - * Draws the shaded regions when "fillGraph" is set. Not to be confused with - * error bars. - * - * For stacked charts, it's more convenient to handle all the series - * simultaneously. So this plotter plots all the points on the first series - * it's asked to draw, then ignores all the other series. - * - * @private - */ -DygraphCanvasRenderer._fillPlotter = function (e) { - // Skip if we're drawing a single series for interactive highlight overlay. - if (e.singleSeriesName) return; - - // We'll handle all the series at once, not one-by-one. - if (e.seriesIndex !== 0) return; - - var g = e.dygraph; - var setNames = g.getLabels().slice(1); // remove x-axis - - // getLabels() includes names for invisible series, which are not included in - // allSeriesPoints. We remove those to make the two match. - // TODO(danvk): provide a simpler way to get this information. - for (var i = setNames.length; i >= 0; i--) { - if (!g.visibility()[i]) setNames.splice(i, 1); - } - - var anySeriesFilled = (function () { - for (var i = 0; i < setNames.length; i++) { - if (g.getBooleanOption("fillGraph", setNames[i])) return true; - } - return false; - })(); - - if (!anySeriesFilled) return; - - var area = e.plotArea; - var sets = e.allSeriesPoints; - var setCount = sets.length; - - var stackedGraph = g.getBooleanOption("stackedGraph"); - var colors = g.getColors(); - - // For stacked graphs, track the baseline for filling. - // - // The filled areas below graph lines are trapezoids with two - // vertical edges. The top edge is the line segment being drawn, and - // the baseline is the bottom edge. Each baseline corresponds to the - // top line segment from the previous stacked line. In the case of - // step plots, the trapezoids are rectangles. - var baseline = {}; - var currBaseline; - var prevStepPlot; // for different line drawing modes (line/step) per series - - // Helper function to trace a line back along the baseline. - var traceBackPath = function traceBackPath(ctx, baselineX, baselineY, pathBack) { - ctx.lineTo(baselineX, baselineY); - if (stackedGraph) { - for (var i = pathBack.length - 1; i >= 0; i--) { - var pt = pathBack[i]; - ctx.lineTo(pt[0], pt[1]); - } - } - }; - - // process sets in reverse order (needed for stacked graphs) - for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) { - var ctx = e.drawingContext; - var setName = setNames[setIdx]; - if (!g.getBooleanOption('fillGraph', setName)) continue; - - var fillAlpha = g.getNumericOption('fillAlpha', setName); - var stepPlot = g.getBooleanOption('stepPlot', setName); - var color = colors[setIdx]; - var axis = g.axisPropertiesForSeries(setName); - var axisY = 1.0 + axis.minyval * axis.yscale; - if (axisY < 0.0) axisY = 0.0;else if (axisY > 1.0) axisY = 1.0; - axisY = area.h * axisY + area.y; - - var points = sets[setIdx]; - var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); - - // setup graphics context - var prevX = NaN; - var prevYs = [-1, -1]; - var newYs; - // should be same color as the lines but only 15% opaque. - var rgb = utils.toRGB_(color); - var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; - ctx.fillStyle = err_color; - ctx.beginPath(); - var last_x, - is_first = true; - - // If the point density is high enough, dropping segments on their way to - // the canvas justifies the overhead of doing so. - if (points.length > 2 * g.width_ || _dygraph2['default'].FORCE_FAST_PROXY) { - ctx = DygraphCanvasRenderer._fastCanvasProxy(ctx); - } - - // For filled charts, we draw points from left to right, then back along - // the x-axis to complete a shape for filling. - // For stacked plots, this "back path" is a more complex shape. This array - // stores the [x, y] values needed to trace that shape. - var pathBack = []; - - // TODO(danvk): there are a lot of options at play in this loop. - // The logic would be much clearer if some (e.g. stackGraph and - // stepPlot) were split off into separate sub-plotters. - var point; - while (iter.hasNext) { - point = iter.next(); - if (!utils.isOK(point.y) && !stepPlot) { - traceBackPath(ctx, prevX, prevYs[1], pathBack); - pathBack = []; - prevX = NaN; - if (point.y_stacked !== null && !isNaN(point.y_stacked)) { - baseline[point.canvasx] = area.h * point.y_stacked + area.y; - } - continue; - } - if (stackedGraph) { - if (!is_first && last_x == point.xval) { - continue; - } else { - is_first = false; - last_x = point.xval; - } - - currBaseline = baseline[point.canvasx]; - var lastY; - if (currBaseline === undefined) { - lastY = axisY; - } else { - if (prevStepPlot) { - lastY = currBaseline[0]; - } else { - lastY = currBaseline; - } - } - newYs = [point.canvasy, lastY]; - - if (stepPlot) { - // Step plots must keep track of the top and bottom of - // the baseline at each point. - if (prevYs[0] === -1) { - baseline[point.canvasx] = [point.canvasy, axisY]; - } else { - baseline[point.canvasx] = [point.canvasy, prevYs[0]]; - } - } else { - baseline[point.canvasx] = point.canvasy; - } - } else { - if (isNaN(point.canvasy) && stepPlot) { - newYs = [area.y + area.h, axisY]; - } else { - newYs = [point.canvasy, axisY]; - } - } - if (!isNaN(prevX)) { - // Move to top fill point - if (stepPlot) { - ctx.lineTo(point.canvasx, prevYs[0]); - ctx.lineTo(point.canvasx, newYs[0]); - } else { - ctx.lineTo(point.canvasx, newYs[0]); - } - - // Record the baseline for the reverse path. - if (stackedGraph) { - pathBack.push([prevX, prevYs[1]]); - if (prevStepPlot && currBaseline) { - // Draw to the bottom of the baseline - pathBack.push([point.canvasx, currBaseline[1]]); - } else { - pathBack.push([point.canvasx, newYs[1]]); - } - } - } else { - ctx.moveTo(point.canvasx, newYs[1]); - ctx.lineTo(point.canvasx, newYs[0]); - } - prevYs = newYs; - prevX = point.canvasx; - } - prevStepPlot = stepPlot; - if (newYs && point) { - traceBackPath(ctx, point.canvasx, newYs[1], pathBack); - pathBack = []; - } - ctx.fill(); - } -}; - -exports['default'] = DygraphCanvasRenderer; -module.exports = exports['default']; - -},{"./dygraph":17,"./dygraph-utils":16}],9:[function(require,module,exports){ -'use strict'; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphTickers = require('./dygraph-tickers'); - -var DygraphTickers = _interopRequireWildcard(_dygraphTickers); - -var _dygraphInteractionModel = require('./dygraph-interaction-model'); - -var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel); - -var _dygraphCanvas = require('./dygraph-canvas'); - -var _dygraphCanvas2 = _interopRequireDefault(_dygraphCanvas); - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -// Default attribute values. -var DEFAULT_ATTRS = { - highlightCircleSize: 3, - highlightSeriesOpts: null, - highlightSeriesBackgroundAlpha: 0.5, - highlightSeriesBackgroundColor: 'rgb(255, 255, 255)', - - labelsDivWidth: 250, - labelsDivStyles: { - // TODO(danvk): move defaults from createStatusMessage_ here. - }, - labelsSeparateLines: false, - labelsShowZeroValues: true, - labelsKMB: false, - labelsKMG2: false, - showLabelsOnHighlight: true, - - digitsAfterDecimal: 2, - maxNumberWidth: 6, - sigFigs: null, - - strokeWidth: 1.0, - strokeBorderWidth: 0, - strokeBorderColor: "white", - - axisTickSize: 3, - axisLabelFontSize: 14, - rightGap: 5, - - showRoller: false, - xValueParser: undefined, - - delimiter: ',', - - sigma: 2.0, - errorBars: false, - fractions: false, - wilsonInterval: true, // only relevant if fractions is true - customBars: false, - fillGraph: false, - fillAlpha: 0.15, - connectSeparatedPoints: false, - - stackedGraph: false, - stackedGraphNaNFill: 'all', - hideOverlayOnMouseOut: true, - - legend: 'onmouseover', - stepPlot: false, - avoidMinZero: false, - xRangePad: 0, - yRangePad: null, - drawAxesAtZero: false, - - // Sizes of the various chart labels. - titleHeight: 28, - xLabelHeight: 18, - yLabelWidth: 18, - - axisLineColor: "black", - axisLineWidth: 0.3, - gridLineWidth: 0.3, - axisLabelColor: "black", - axisLabelWidth: 50, - gridLineColor: "rgb(128,128,128)", - - interactionModel: _dygraphInteractionModel2['default'].defaultModel, - animatedZooms: false, // (for now) - - // Range selector options - showRangeSelector: false, - rangeSelectorHeight: 40, - rangeSelectorPlotStrokeColor: "#808FAB", - rangeSelectorPlotFillGradientColor: "white", - rangeSelectorPlotFillColor: "#A7B1C4", - rangeSelectorBackgroundStrokeColor: "gray", - rangeSelectorBackgroundLineWidth: 1, - rangeSelectorPlotLineWidth: 1.5, - rangeSelectorForegroundStrokeColor: "black", - rangeSelectorForegroundLineWidth: 1, - rangeSelectorAlpha: 0.6, - showInRangeSelector: null, - - // The ordering here ensures that central lines always appear above any - // fill bars/error bars. - plotter: [_dygraphCanvas2['default']._fillPlotter, _dygraphCanvas2['default']._errorPlotter, _dygraphCanvas2['default']._linePlotter], - - plugins: [], - - // per-axis options - axes: { - x: { - pixelsPerLabel: 70, - axisLabelWidth: 60, - axisLabelFormatter: utils.dateAxisLabelFormatter, - valueFormatter: utils.dateValueFormatter, - drawGrid: true, - drawAxis: true, - independentTicks: true, - ticker: DygraphTickers.dateTicker - }, - y: { - axisLabelWidth: 50, - pixelsPerLabel: 30, - valueFormatter: utils.numberValueFormatter, - axisLabelFormatter: utils.numberAxisLabelFormatter, - drawGrid: true, - drawAxis: true, - independentTicks: true, - ticker: DygraphTickers.numericTicks - }, - y2: { - axisLabelWidth: 50, - pixelsPerLabel: 30, - valueFormatter: utils.numberValueFormatter, - axisLabelFormatter: utils.numberAxisLabelFormatter, - drawAxis: true, // only applies when there are two axes of data. - drawGrid: false, - independentTicks: false, - ticker: DygraphTickers.numericTicks - } - } -}; - -exports['default'] = DEFAULT_ATTRS; -module.exports = exports['default']; - -},{"./dygraph-canvas":8,"./dygraph-interaction-model":11,"./dygraph-tickers":15,"./dygraph-utils":16}],10:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview A wrapper around the Dygraph class which implements the - * interface for a GViz (aka Google Visualization API) visualization. - * It is designed to be a drop-in replacement for Google's AnnotatedTimeline, - * so the documentation at - * http://code.google.com/apis/chart/interactive/docs/gallery/annotatedtimeline.html - * translates over directly. - * - * For a full demo, see: - * - http://dygraphs.com/tests/gviz.html - * - http://dygraphs.com/tests/annotation-gviz.html - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -var _dygraph = require('./dygraph'); - -var _dygraph2 = _interopRequireDefault(_dygraph); - -/** - * A wrapper around Dygraph that implements the gviz API. - * @param {!HTMLDivElement} container The DOM object the visualization should - * live in. - * @constructor - */ -var GVizChart = function GVizChart(container) { - this.container = container; -}; - -/** - * @param {GVizDataTable} data - * @param {Object.<*>} options - */ -GVizChart.prototype.draw = function (data, options) { - // Clear out any existing dygraph. - // TODO(danvk): would it make more sense to simply redraw using the current - // date_graph object? - this.container.innerHTML = ''; - if (typeof this.date_graph != 'undefined') { - this.date_graph.destroy(); - } - - this.date_graph = new _dygraph2['default'](this.container, data, options); -}; - -/** - * Google charts compatible setSelection - * Only row selection is supported, all points in the row will be highlighted - * @param {Array.<{row:number}>} selection_array array of the selected cells - * @public - */ -GVizChart.prototype.setSelection = function (selection_array) { - var row = false; - if (selection_array.length) { - row = selection_array[0].row; - } - this.date_graph.setSelection(row); -}; - -/** - * Google charts compatible getSelection implementation - * @return {Array.<{row:number,column:number}>} array of the selected cells - * @public - */ -GVizChart.prototype.getSelection = function () { - var selection = []; - - var row = this.date_graph.getSelection(); - - if (row < 0) return selection; - - var points = this.date_graph.layout_.points; - for (var setIdx = 0; setIdx < points.length; ++setIdx) { - selection.push({ row: row, column: setIdx + 1 }); - } - - return selection; -}; - -exports['default'] = GVizChart; -module.exports = exports['default']; - -},{"./dygraph":17}],11:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Robert Konigsberg (konigsberg@google.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview The default interaction model for Dygraphs. This is kept out - * of dygraph.js for better navigability. - * @author Robert Konigsberg (konigsberg@google.com) - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** - * You can drag this many pixels past the edge of the chart and still have it - * be considered a zoom. This makes it easier to zoom to the exact edge of the - * chart, a fairly common operation. - */ -var DRAG_EDGE_MARGIN = 100; - -/** - * A collection of functions to facilitate build custom interaction models. - * @class - */ -var DygraphInteraction = {}; - -/** - * Checks whether the beginning & ending of an event were close enough that it - * should be considered a click. If it should, dispatch appropriate events. - * Returns true if the event was treated as a click. - * - * @param {Event} event - * @param {Dygraph} g - * @param {Object} context - */ -DygraphInteraction.maybeTreatMouseOpAsClick = function (event, g, context) { - context.dragEndX = utils.dragGetX_(event, context); - context.dragEndY = utils.dragGetY_(event, context); - var regionWidth = Math.abs(context.dragEndX - context.dragStartX); - var regionHeight = Math.abs(context.dragEndY - context.dragStartY); - - if (regionWidth < 2 && regionHeight < 2 && g.lastx_ !== undefined && g.lastx_ != -1) { - DygraphInteraction.treatMouseOpAsClick(g, event, context); - } - - context.regionWidth = regionWidth; - context.regionHeight = regionHeight; -}; - -/** - * Called in response to an interaction model operation that - * should start the default panning behavior. - * - * It's used in the default callback for "mousedown" operations. - * Custom interaction model builders can use it to provide the default - * panning behavior. - * - * @param {Event} event the event object which led to the startPan call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.startPan = function (event, g, context) { - var i, axis; - context.isPanning = true; - var xRange = g.xAxisRange(); - - if (g.getOptionForAxis("logscale", "x")) { - context.initialLeftmostDate = utils.log10(xRange[0]); - context.dateRange = utils.log10(xRange[1]) - utils.log10(xRange[0]); - } else { - context.initialLeftmostDate = xRange[0]; - context.dateRange = xRange[1] - xRange[0]; - } - context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1); - - if (g.getNumericOption("panEdgeFraction")) { - var maxXPixelsToDraw = g.width_ * g.getNumericOption("panEdgeFraction"); - var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes! - - var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw; - var boundedRightX = g.toDomXCoord(xExtremes[1]) + maxXPixelsToDraw; - - var boundedLeftDate = g.toDataXCoord(boundedLeftX); - var boundedRightDate = g.toDataXCoord(boundedRightX); - context.boundedDates = [boundedLeftDate, boundedRightDate]; - - var boundedValues = []; - var maxYPixelsToDraw = g.height_ * g.getNumericOption("panEdgeFraction"); - - for (i = 0; i < g.axes_.length; i++) { - axis = g.axes_[i]; - var yExtremes = axis.extremeRange; - - var boundedTopY = g.toDomYCoord(yExtremes[0], i) + maxYPixelsToDraw; - var boundedBottomY = g.toDomYCoord(yExtremes[1], i) - maxYPixelsToDraw; - - var boundedTopValue = g.toDataYCoord(boundedTopY, i); - var boundedBottomValue = g.toDataYCoord(boundedBottomY, i); - - boundedValues[i] = [boundedTopValue, boundedBottomValue]; - } - context.boundedValues = boundedValues; - } - - // Record the range of each y-axis at the start of the drag. - // If any axis has a valueRange or valueWindow, then we want a 2D pan. - // We can't store data directly in g.axes_, because it does not belong to us - // and could change out from under us during a pan (say if there's a data - // update). - context.is2DPan = false; - context.axes = []; - for (i = 0; i < g.axes_.length; i++) { - axis = g.axes_[i]; - var axis_data = {}; - var yRange = g.yAxisRange(i); - // TODO(konigsberg): These values should be in |context|. - // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale. - var logscale = g.attributes_.getForAxis("logscale", i); - if (logscale) { - axis_data.initialTopValue = utils.log10(yRange[1]); - axis_data.dragValueRange = utils.log10(yRange[1]) - utils.log10(yRange[0]); - } else { - axis_data.initialTopValue = yRange[1]; - axis_data.dragValueRange = yRange[1] - yRange[0]; - } - axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1); - context.axes.push(axis_data); - - // While calculating axes, set 2dpan. - if (axis.valueWindow || axis.valueRange) context.is2DPan = true; - } -}; - -/** - * Called in response to an interaction model operation that - * responds to an event that pans the view. - * - * It's used in the default callback for "mousemove" operations. - * Custom interaction model builders can use it to provide the default - * panning behavior. - * - * @param {Event} event the event object which led to the movePan call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.movePan = function (event, g, context) { - context.dragEndX = utils.dragGetX_(event, context); - context.dragEndY = utils.dragGetY_(event, context); - - var minDate = context.initialLeftmostDate - (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel; - if (context.boundedDates) { - minDate = Math.max(minDate, context.boundedDates[0]); - } - var maxDate = minDate + context.dateRange; - if (context.boundedDates) { - if (maxDate > context.boundedDates[1]) { - // Adjust minDate, and recompute maxDate. - minDate = minDate - (maxDate - context.boundedDates[1]); - maxDate = minDate + context.dateRange; - } - } - - if (g.getOptionForAxis("logscale", "x")) { - g.dateWindow_ = [Math.pow(utils.LOG_SCALE, minDate), Math.pow(utils.LOG_SCALE, maxDate)]; - } else { - g.dateWindow_ = [minDate, maxDate]; - } - - // y-axis scaling is automatic unless this is a full 2D pan. - if (context.is2DPan) { - - var pixelsDragged = context.dragEndY - context.dragStartY; - - // Adjust each axis appropriately. - for (var i = 0; i < g.axes_.length; i++) { - var axis = g.axes_[i]; - var axis_data = context.axes[i]; - var unitsDragged = pixelsDragged * axis_data.unitsPerPixel; - - var boundedValue = context.boundedValues ? context.boundedValues[i] : null; - - // In log scale, maxValue and minValue are the logs of those values. - var maxValue = axis_data.initialTopValue + unitsDragged; - if (boundedValue) { - maxValue = Math.min(maxValue, boundedValue[1]); - } - var minValue = maxValue - axis_data.dragValueRange; - if (boundedValue) { - if (minValue < boundedValue[0]) { - // Adjust maxValue, and recompute minValue. - maxValue = maxValue - (minValue - boundedValue[0]); - minValue = maxValue - axis_data.dragValueRange; - } - } - if (g.attributes_.getForAxis("logscale", i)) { - axis.valueWindow = [Math.pow(utils.LOG_SCALE, minValue), Math.pow(utils.LOG_SCALE, maxValue)]; - } else { - axis.valueWindow = [minValue, maxValue]; - } - } - } - - g.drawGraph_(false); -}; - -/** - * Called in response to an interaction model operation that - * responds to an event that ends panning. - * - * It's used in the default callback for "mouseup" operations. - * Custom interaction model builders can use it to provide the default - * panning behavior. - * - * @param {Event} event the event object which led to the endPan call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.endPan = DygraphInteraction.maybeTreatMouseOpAsClick; - -/** - * Called in response to an interaction model operation that - * responds to an event that starts zooming. - * - * It's used in the default callback for "mousedown" operations. - * Custom interaction model builders can use it to provide the default - * zooming behavior. - * - * @param {Event} event the event object which led to the startZoom call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.startZoom = function (event, g, context) { - context.isZooming = true; - context.zoomMoved = false; -}; - -/** - * Called in response to an interaction model operation that - * responds to an event that defines zoom boundaries. - * - * It's used in the default callback for "mousemove" operations. - * Custom interaction model builders can use it to provide the default - * zooming behavior. - * - * @param {Event} event the event object which led to the moveZoom call. - * @param {Dygraph} g The dygraph on which to act. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.moveZoom = function (event, g, context) { - context.zoomMoved = true; - context.dragEndX = utils.dragGetX_(event, context); - context.dragEndY = utils.dragGetY_(event, context); - - var xDelta = Math.abs(context.dragStartX - context.dragEndX); - var yDelta = Math.abs(context.dragStartY - context.dragEndY); - - // drag direction threshold for y axis is twice as large as x axis - context.dragDirection = xDelta < yDelta / 2 ? utils.VERTICAL : utils.HORIZONTAL; - - g.drawZoomRect_(context.dragDirection, context.dragStartX, context.dragEndX, context.dragStartY, context.dragEndY, context.prevDragDirection, context.prevEndX, context.prevEndY); - - context.prevEndX = context.dragEndX; - context.prevEndY = context.dragEndY; - context.prevDragDirection = context.dragDirection; -}; - -/** - * TODO(danvk): move this logic into dygraph.js - * @param {Dygraph} g - * @param {Event} event - * @param {Object} context - */ -DygraphInteraction.treatMouseOpAsClick = function (g, event, context) { - var clickCallback = g.getFunctionOption('clickCallback'); - var pointClickCallback = g.getFunctionOption('pointClickCallback'); - - var selectedPoint = null; - - // Find out if the click occurs on a point. - var closestIdx = -1; - var closestDistance = Number.MAX_VALUE; - - // check if the click was on a particular point. - for (var i = 0; i < g.selPoints_.length; i++) { - var p = g.selPoints_[i]; - var distance = Math.pow(p.canvasx - context.dragEndX, 2) + Math.pow(p.canvasy - context.dragEndY, 2); - if (!isNaN(distance) && (closestIdx == -1 || distance < closestDistance)) { - closestDistance = distance; - closestIdx = i; - } - } - - // Allow any click within two pixels of the dot. - var radius = g.getNumericOption('highlightCircleSize') + 2; - if (closestDistance <= radius * radius) { - selectedPoint = g.selPoints_[closestIdx]; - } - - if (selectedPoint) { - var e = { - cancelable: true, - point: selectedPoint, - canvasx: context.dragEndX, - canvasy: context.dragEndY - }; - var defaultPrevented = g.cascadeEvents_('pointClick', e); - if (defaultPrevented) { - // Note: this also prevents click / clickCallback from firing. - return; - } - if (pointClickCallback) { - pointClickCallback.call(g, event, selectedPoint); - } - } - - var e = { - cancelable: true, - xval: g.lastx_, // closest point by x value - pts: g.selPoints_, - canvasx: context.dragEndX, - canvasy: context.dragEndY - }; - if (!g.cascadeEvents_('click', e)) { - if (clickCallback) { - // TODO(danvk): pass along more info about the points, e.g. 'x' - clickCallback.call(g, event, g.lastx_, g.selPoints_); - } - } -}; - -/** - * Called in response to an interaction model operation that - * responds to an event that performs a zoom based on previously defined - * bounds.. - * - * It's used in the default callback for "mouseup" operations. - * Custom interaction model builders can use it to provide the default - * zooming behavior. - * - * @param {Event} event the event object which led to the endZoom call. - * @param {Dygraph} g The dygraph on which to end the zoom. - * @param {Object} context The dragging context object (with - * dragStartX/dragStartY/etc. properties). This function modifies the - * context. - */ -DygraphInteraction.endZoom = function (event, g, context) { - g.clearZoomRect_(); - context.isZooming = false; - DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context); - - // The zoom rectangle is visibly clipped to the plot area, so its behavior - // should be as well. - // See http://code.google.com/p/dygraphs/issues/detail?id=280 - var plotArea = g.getArea(); - if (context.regionWidth >= 10 && context.dragDirection == utils.HORIZONTAL) { - var left = Math.min(context.dragStartX, context.dragEndX), - right = Math.max(context.dragStartX, context.dragEndX); - left = Math.max(left, plotArea.x); - right = Math.min(right, plotArea.x + plotArea.w); - if (left < right) { - g.doZoomX_(left, right); - } - context.cancelNextDblclick = true; - } else if (context.regionHeight >= 10 && context.dragDirection == utils.VERTICAL) { - var top = Math.min(context.dragStartY, context.dragEndY), - bottom = Math.max(context.dragStartY, context.dragEndY); - top = Math.max(top, plotArea.y); - bottom = Math.min(bottom, plotArea.y + plotArea.h); - if (top < bottom) { - g.doZoomY_(top, bottom); - } - context.cancelNextDblclick = true; - } - context.dragStartX = null; - context.dragStartY = null; -}; - -/** - * @private - */ -DygraphInteraction.startTouch = function (event, g, context) { - event.preventDefault(); // touch browsers are all nice. - if (event.touches.length > 1) { - // If the user ever puts two fingers down, it's not a double tap. - context.startTimeForDoubleTapMs = null; - } - - var touches = []; - for (var i = 0; i < event.touches.length; i++) { - var t = event.touches[i]; - // we dispense with 'dragGetX_' because all touchBrowsers support pageX - touches.push({ - pageX: t.pageX, - pageY: t.pageY, - dataX: g.toDataXCoord(t.pageX), - dataY: g.toDataYCoord(t.pageY) - // identifier: t.identifier - }); - } - context.initialTouches = touches; - - if (touches.length == 1) { - // This is just a swipe. - context.initialPinchCenter = touches[0]; - context.touchDirections = { x: true, y: true }; - } else if (touches.length >= 2) { - // It's become a pinch! - // In case there are 3+ touches, we ignore all but the "first" two. - - // only screen coordinates can be averaged (data coords could be log scale). - context.initialPinchCenter = { - pageX: 0.5 * (touches[0].pageX + touches[1].pageX), - pageY: 0.5 * (touches[0].pageY + touches[1].pageY), - - // TODO(danvk): remove - dataX: 0.5 * (touches[0].dataX + touches[1].dataX), - dataY: 0.5 * (touches[0].dataY + touches[1].dataY) - }; - - // Make pinches in a 45-degree swath around either axis 1-dimensional zooms. - var initialAngle = 180 / Math.PI * Math.atan2(context.initialPinchCenter.pageY - touches[0].pageY, touches[0].pageX - context.initialPinchCenter.pageX); - - // use symmetry to get it into the first quadrant. - initialAngle = Math.abs(initialAngle); - if (initialAngle > 90) initialAngle = 90 - initialAngle; - - context.touchDirections = { - x: initialAngle < 90 - 45 / 2, - y: initialAngle > 45 / 2 - }; - } - - // save the full x & y ranges. - context.initialRange = { - x: g.xAxisRange(), - y: g.yAxisRange() - }; -}; - -/** - * @private - */ -DygraphInteraction.moveTouch = function (event, g, context) { - // If the tap moves, then it's definitely not part of a double-tap. - context.startTimeForDoubleTapMs = null; - - var i, - touches = []; - for (i = 0; i < event.touches.length; i++) { - var t = event.touches[i]; - touches.push({ - pageX: t.pageX, - pageY: t.pageY - }); - } - var initialTouches = context.initialTouches; - - var c_now; - - // old and new centers. - var c_init = context.initialPinchCenter; - if (touches.length == 1) { - c_now = touches[0]; - } else { - c_now = { - pageX: 0.5 * (touches[0].pageX + touches[1].pageX), - pageY: 0.5 * (touches[0].pageY + touches[1].pageY) - }; - } - - // this is the "swipe" component - // we toss it out for now, but could use it in the future. - var swipe = { - pageX: c_now.pageX - c_init.pageX, - pageY: c_now.pageY - c_init.pageY - }; - var dataWidth = context.initialRange.x[1] - context.initialRange.x[0]; - var dataHeight = context.initialRange.y[0] - context.initialRange.y[1]; - swipe.dataX = swipe.pageX / g.plotter_.area.w * dataWidth; - swipe.dataY = swipe.pageY / g.plotter_.area.h * dataHeight; - var xScale, yScale; - - // The residual bits are usually split into scale & rotate bits, but we split - // them into x-scale and y-scale bits. - if (touches.length == 1) { - xScale = 1.0; - yScale = 1.0; - } else if (touches.length >= 2) { - var initHalfWidth = initialTouches[1].pageX - c_init.pageX; - xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth; - - var initHalfHeight = initialTouches[1].pageY - c_init.pageY; - yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight; - } - - // Clip scaling to [1/8, 8] to prevent too much blowup. - xScale = Math.min(8, Math.max(0.125, xScale)); - yScale = Math.min(8, Math.max(0.125, yScale)); - - var didZoom = false; - if (context.touchDirections.x) { - g.dateWindow_ = [c_init.dataX - swipe.dataX + (context.initialRange.x[0] - c_init.dataX) / xScale, c_init.dataX - swipe.dataX + (context.initialRange.x[1] - c_init.dataX) / xScale]; - didZoom = true; - } - - if (context.touchDirections.y) { - for (i = 0; i < 1 /*g.axes_.length*/; i++) { - var axis = g.axes_[i]; - var logscale = g.attributes_.getForAxis("logscale", i); - if (logscale) { - // TODO(danvk): implement - } else { - axis.valueWindow = [c_init.dataY - swipe.dataY + (context.initialRange.y[0] - c_init.dataY) / yScale, c_init.dataY - swipe.dataY + (context.initialRange.y[1] - c_init.dataY) / yScale]; - didZoom = true; - } - } - } - - g.drawGraph_(false); - - // We only call zoomCallback on zooms, not pans, to mirror desktop behavior. - if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) { - var viewWindow = g.xAxisRange(); - g.getFunctionOption("zoomCallback").call(g, viewWindow[0], viewWindow[1], g.yAxisRanges()); - } -}; - -/** - * @private - */ -DygraphInteraction.endTouch = function (event, g, context) { - if (event.touches.length !== 0) { - // this is effectively a "reset" - DygraphInteraction.startTouch(event, g, context); - } else if (event.changedTouches.length == 1) { - // Could be part of a "double tap" - // The heuristic here is that it's a double-tap if the two touchend events - // occur within 500ms and within a 50x50 pixel box. - var now = new Date().getTime(); - var t = event.changedTouches[0]; - if (context.startTimeForDoubleTapMs && now - context.startTimeForDoubleTapMs < 500 && context.doubleTapX && Math.abs(context.doubleTapX - t.screenX) < 50 && context.doubleTapY && Math.abs(context.doubleTapY - t.screenY) < 50) { - g.resetZoom(); - } else { - context.startTimeForDoubleTapMs = now; - context.doubleTapX = t.screenX; - context.doubleTapY = t.screenY; - } - } -}; - -// Determine the distance from x to [left, right]. -var distanceFromInterval = function distanceFromInterval(x, left, right) { - if (x < left) { - return left - x; - } else if (x > right) { - return x - right; - } else { - return 0; - } -}; - -/** - * Returns the number of pixels by which the event happens from the nearest - * edge of the chart. For events in the interior of the chart, this returns zero. - */ -var distanceFromChart = function distanceFromChart(event, g) { - var chartPos = utils.findPos(g.canvas_); - var box = { - left: chartPos.x, - right: chartPos.x + g.canvas_.offsetWidth, - top: chartPos.y, - bottom: chartPos.y + g.canvas_.offsetHeight - }; - - var pt = { - x: utils.pageX(event), - y: utils.pageY(event) - }; - - var dx = distanceFromInterval(pt.x, box.left, box.right), - dy = distanceFromInterval(pt.y, box.top, box.bottom); - return Math.max(dx, dy); -}; - -/** - * Default interation model for dygraphs. You can refer to specific elements of - * this when constructing your own interaction model, e.g.: - * g.updateOptions( { - * interactionModel: { - * mousedown: DygraphInteraction.defaultInteractionModel.mousedown - * } - * } ); - */ -DygraphInteraction.defaultModel = { - // Track the beginning of drag events - mousedown: function mousedown(event, g, context) { - // Right-click should not initiate a zoom. - if (event.button && event.button == 2) return; - - context.initializeMouseDown(event, g, context); - - if (event.altKey || event.shiftKey) { - DygraphInteraction.startPan(event, g, context); - } else { - DygraphInteraction.startZoom(event, g, context); - } - - // Note: we register mousemove/mouseup on document to allow some leeway for - // events to move outside of the chart. Interaction model events get - // registered on the canvas, which is too small to allow this. - var mousemove = function mousemove(event) { - if (context.isZooming) { - // When the mouse moves >200px from the chart edge, cancel the zoom. - var d = distanceFromChart(event, g); - if (d < DRAG_EDGE_MARGIN) { - DygraphInteraction.moveZoom(event, g, context); - } else { - if (context.dragEndX !== null) { - context.dragEndX = null; - context.dragEndY = null; - g.clearZoomRect_(); - } - } - } else if (context.isPanning) { - DygraphInteraction.movePan(event, g, context); - } - }; - var mouseup = function mouseup(event) { - if (context.isZooming) { - if (context.dragEndX !== null) { - DygraphInteraction.endZoom(event, g, context); - } else { - DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context); - } - } else if (context.isPanning) { - DygraphInteraction.endPan(event, g, context); - } - - utils.removeEvent(document, 'mousemove', mousemove); - utils.removeEvent(document, 'mouseup', mouseup); - context.destroy(); - }; - - g.addAndTrackEvent(document, 'mousemove', mousemove); - g.addAndTrackEvent(document, 'mouseup', mouseup); - }, - willDestroyContextMyself: true, - - touchstart: function touchstart(event, g, context) { - DygraphInteraction.startTouch(event, g, context); - }, - touchmove: function touchmove(event, g, context) { - DygraphInteraction.moveTouch(event, g, context); - }, - touchend: function touchend(event, g, context) { - DygraphInteraction.endTouch(event, g, context); - }, - - // Disable zooming out if panning. - dblclick: function dblclick(event, g, context) { - if (context.cancelNextDblclick) { - context.cancelNextDblclick = false; - return; - } - - // Give plugins a chance to grab this event. - var e = { - canvasx: context.dragEndX, - canvasy: context.dragEndY - }; - if (g.cascadeEvents_('dblclick', e)) { - return; - } - - if (event.altKey || event.shiftKey) { - return; - } - g.resetZoom(); - } -}; - -/* -Dygraph.DEFAULT_ATTRS.interactionModel = DygraphInteraction.defaultModel; - -// old ways of accessing these methods/properties -Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel; -Dygraph.endZoom = DygraphInteraction.endZoom; -Dygraph.moveZoom = DygraphInteraction.moveZoom; -Dygraph.startZoom = DygraphInteraction.startZoom; -Dygraph.endPan = DygraphInteraction.endPan; -Dygraph.movePan = DygraphInteraction.movePan; -Dygraph.startPan = DygraphInteraction.startPan; -*/ - -DygraphInteraction.nonInteractiveModel_ = { - mousedown: function mousedown(event, g, context) { - context.initializeMouseDown(event, g, context); - }, - mouseup: DygraphInteraction.maybeTreatMouseOpAsClick -}; - -// Default interaction model when using the range selector. -DygraphInteraction.dragIsPanInteractionModel = { - mousedown: function mousedown(event, g, context) { - context.initializeMouseDown(event, g, context); - DygraphInteraction.startPan(event, g, context); - }, - mousemove: function mousemove(event, g, context) { - if (context.isPanning) { - DygraphInteraction.movePan(event, g, context); - } - }, - mouseup: function mouseup(event, g, context) { - if (context.isPanning) { - DygraphInteraction.endPan(event, g, context); - } - } -}; - -exports["default"] = DygraphInteraction; -module.exports = exports["default"]; - -},{"./dygraph-utils":16}],12:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview Based on PlotKitLayout, but modified to meet the needs of - * dygraphs. - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** - * Creates a new DygraphLayout object. - * - * This class contains all the data to be charted. - * It uses data coordinates, but also records the chart range (in data - * coordinates) and hence is able to calculate percentage positions ('In this - * view, Point A lies 25% down the x-axis.') - * - * Two things that it does not do are: - * 1. Record pixel coordinates for anything. - * 2. (oddly) determine anything about the layout of chart elements. - * - * The naming is a vestige of Dygraph's original PlotKit roots. - * - * @constructor - */ -var DygraphLayout = function DygraphLayout(dygraph) { - this.dygraph_ = dygraph; - /** - * Array of points for each series. - * - * [series index][row index in series] = |Point| structure, - * where series index refers to visible series only, and the - * point index is for the reduced set of points for the current - * zoom region (including one point just outside the window). - * All points in the same row index share the same X value. - * - * @type {Array.>} - */ - this.points = []; - this.setNames = []; - this.annotations = []; - this.yAxes_ = null; - - // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs, but xticks and - // yticks are outputs. Clean this up. - this.xTicks_ = null; - this.yTicks_ = null; -}; - -/** - * Add points for a single series. - * - * @param {string} setname Name of the series. - * @param {Array.} set_xy Points for the series. - */ -DygraphLayout.prototype.addDataset = function (setname, set_xy) { - this.points.push(set_xy); - this.setNames.push(setname); -}; - -/** - * Returns the box which the chart should be drawn in. This is the canvas's - * box, less space needed for the axis and chart labels. - * - * @return {{x: number, y: number, w: number, h: number}} - */ -DygraphLayout.prototype.getPlotArea = function () { - return this.area_; -}; - -// Compute the box which the chart should be drawn in. This is the canvas's -// box, less space needed for axis, chart labels, and other plug-ins. -// NOTE: This should only be called by Dygraph.predraw_(). -DygraphLayout.prototype.computePlotArea = function () { - var area = { - // TODO(danvk): per-axis setting. - x: 0, - y: 0 - }; - - area.w = this.dygraph_.width_ - area.x - this.dygraph_.getOption('rightGap'); - area.h = this.dygraph_.height_; - - // Let plugins reserve space. - var e = { - chart_div: this.dygraph_.graphDiv, - reserveSpaceLeft: function reserveSpaceLeft(px) { - var r = { - x: area.x, - y: area.y, - w: px, - h: area.h - }; - area.x += px; - area.w -= px; - return r; - }, - reserveSpaceRight: function reserveSpaceRight(px) { - var r = { - x: area.x + area.w - px, - y: area.y, - w: px, - h: area.h - }; - area.w -= px; - return r; - }, - reserveSpaceTop: function reserveSpaceTop(px) { - var r = { - x: area.x, - y: area.y, - w: area.w, - h: px - }; - area.y += px; - area.h -= px; - return r; - }, - reserveSpaceBottom: function reserveSpaceBottom(px) { - var r = { - x: area.x, - y: area.y + area.h - px, - w: area.w, - h: px - }; - area.h -= px; - return r; - }, - chartRect: function chartRect() { - return { x: area.x, y: area.y, w: area.w, h: area.h }; - } - }; - this.dygraph_.cascadeEvents_('layout', e); - - this.area_ = area; -}; - -DygraphLayout.prototype.setAnnotations = function (ann) { - // The Dygraph object's annotations aren't parsed. We parse them here and - // save a copy. If there is no parser, then the user must be using raw format. - this.annotations = []; - var parse = this.dygraph_.getOption('xValueParser') || function (x) { - return x; - }; - for (var i = 0; i < ann.length; i++) { - var a = {}; - if (!ann[i].xval && ann[i].x === undefined) { - console.error("Annotations must have an 'x' property"); - return; - } - if (ann[i].icon && !(ann[i].hasOwnProperty('width') && ann[i].hasOwnProperty('height'))) { - console.error("Must set width and height when setting " + "annotation.icon property"); - return; - } - utils.update(a, ann[i]); - if (!a.xval) a.xval = parse(a.x); - this.annotations.push(a); - } -}; - -DygraphLayout.prototype.setXTicks = function (xTicks) { - this.xTicks_ = xTicks; -}; - -// TODO(danvk): add this to the Dygraph object's API or move it into Layout. -DygraphLayout.prototype.setYAxes = function (yAxes) { - this.yAxes_ = yAxes; -}; - -DygraphLayout.prototype.evaluate = function () { - this._xAxis = {}; - this._evaluateLimits(); - this._evaluateLineCharts(); - this._evaluateLineTicks(); - this._evaluateAnnotations(); -}; - -DygraphLayout.prototype._evaluateLimits = function () { - var xlimits = this.dygraph_.xAxisRange(); - this._xAxis.minval = xlimits[0]; - this._xAxis.maxval = xlimits[1]; - var xrange = xlimits[1] - xlimits[0]; - this._xAxis.scale = xrange !== 0 ? 1 / xrange : 1.0; - - if (this.dygraph_.getOptionForAxis("logscale", 'x')) { - this._xAxis.xlogrange = utils.log10(this._xAxis.maxval) - utils.log10(this._xAxis.minval); - this._xAxis.xlogscale = this._xAxis.xlogrange !== 0 ? 1.0 / this._xAxis.xlogrange : 1.0; - } - for (var i = 0; i < this.yAxes_.length; i++) { - var axis = this.yAxes_[i]; - axis.minyval = axis.computedValueRange[0]; - axis.maxyval = axis.computedValueRange[1]; - axis.yrange = axis.maxyval - axis.minyval; - axis.yscale = axis.yrange !== 0 ? 1.0 / axis.yrange : 1.0; - - if (this.dygraph_.getOption("logscale")) { - axis.ylogrange = utils.log10(axis.maxyval) - utils.log10(axis.minyval); - axis.ylogscale = axis.ylogrange !== 0 ? 1.0 / axis.ylogrange : 1.0; - if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) { - console.error('axis ' + i + ' of graph at ' + axis.g + ' can\'t be displayed in log scale for range [' + axis.minyval + ' - ' + axis.maxyval + ']'); - } - } - } -}; - -DygraphLayout.calcXNormal_ = function (value, xAxis, logscale) { - if (logscale) { - return (utils.log10(value) - utils.log10(xAxis.minval)) * xAxis.xlogscale; - } else { - return (value - xAxis.minval) * xAxis.scale; - } -}; - -/** - * @param {DygraphAxisType} axis - * @param {number} value - * @param {boolean} logscale - * @return {number} - */ -DygraphLayout.calcYNormal_ = function (axis, value, logscale) { - if (logscale) { - var x = 1.0 - (utils.log10(value) - utils.log10(axis.minyval)) * axis.ylogscale; - return isFinite(x) ? x : NaN; // shim for v8 issue; see pull request 276 - } else { - return 1.0 - (value - axis.minyval) * axis.yscale; - } -}; - -DygraphLayout.prototype._evaluateLineCharts = function () { - var isStacked = this.dygraph_.getOption("stackedGraph"); - var isLogscaleForX = this.dygraph_.getOptionForAxis("logscale", 'x'); - - for (var setIdx = 0; setIdx < this.points.length; setIdx++) { - var points = this.points[setIdx]; - var setName = this.setNames[setIdx]; - var connectSeparated = this.dygraph_.getOption('connectSeparatedPoints', setName); - var axis = this.dygraph_.axisPropertiesForSeries(setName); - // TODO (konigsberg): use optionsForAxis instead. - var logscale = this.dygraph_.attributes_.getForSeries("logscale", setName); - - for (var j = 0; j < points.length; j++) { - var point = points[j]; - - // Range from 0-1 where 0 represents left and 1 represents right. - point.x = DygraphLayout.calcXNormal_(point.xval, this._xAxis, isLogscaleForX); - // Range from 0-1 where 0 represents top and 1 represents bottom - var yval = point.yval; - if (isStacked) { - point.y_stacked = DygraphLayout.calcYNormal_(axis, point.yval_stacked, logscale); - if (yval !== null && !isNaN(yval)) { - yval = point.yval_stacked; - } - } - if (yval === null) { - yval = NaN; - if (!connectSeparated) { - point.yval = NaN; - } - } - point.y = DygraphLayout.calcYNormal_(axis, yval, logscale); - } - - this.dygraph_.dataHandler_.onLineEvaluated(points, axis, logscale); - } -}; - -DygraphLayout.prototype._evaluateLineTicks = function () { - var i, tick, label, pos; - this.xticks = []; - for (i = 0; i < this.xTicks_.length; i++) { - tick = this.xTicks_[i]; - label = tick.label; - pos = this.dygraph_.toPercentXCoord(tick.v); - if (pos >= 0.0 && pos < 1.0) { - this.xticks.push([pos, label]); - } - } - - this.yticks = []; - for (i = 0; i < this.yAxes_.length; i++) { - var axis = this.yAxes_[i]; - for (var j = 0; j < axis.ticks.length; j++) { - tick = axis.ticks[j]; - label = tick.label; - pos = this.dygraph_.toPercentYCoord(tick.v, i); - if (pos > 0.0 && pos <= 1.0) { - this.yticks.push([i, pos, label]); - } - } - } -}; - -DygraphLayout.prototype._evaluateAnnotations = function () { - // Add the annotations to the point to which they belong. - // Make a map from (setName, xval) to annotation for quick lookups. - var i; - var annotations = {}; - for (i = 0; i < this.annotations.length; i++) { - var a = this.annotations[i]; - annotations[a.xval + "," + a.series] = a; - } - - this.annotated_points = []; - - // Exit the function early if there are no annotations. - if (!this.annotations || !this.annotations.length) { - return; - } - - // TODO(antrob): loop through annotations not points. - for (var setIdx = 0; setIdx < this.points.length; setIdx++) { - var points = this.points[setIdx]; - for (i = 0; i < points.length; i++) { - var p = points[i]; - var k = p.xval + "," + p.name; - if (k in annotations) { - p.annotation = annotations[k]; - this.annotated_points.push(p); - } - } - } -}; - -/** - * Convenience function to remove all the data sets from a graph - */ -DygraphLayout.prototype.removeAllDatasets = function () { - delete this.points; - delete this.setNames; - delete this.setPointsLengths; - delete this.setPointsOffsets; - this.points = []; - this.setNames = []; - this.setPointsLengths = []; - this.setPointsOffsets = []; -}; - -exports['default'] = DygraphLayout; -module.exports = exports['default']; - -},{"./dygraph-utils":16}],13:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var OPTIONS_REFERENCE = null; - -// For "production" code, this gets removed by uglifyjs. -if ("development" != 'production') { - - // NOTE: in addition to parsing as JS, this snippet is expected to be valid - // JSON. This assumption cannot be checked in JS, but it will be checked when - // documentation is generated by the generate-documentation.py script. For the - // most part, this just means that you should always use double quotes. - OPTIONS_REFERENCE = // - { - "xValueParser": { - "default": "parseFloat() or Date.parse()*", - "labels": ["CSV parsing"], - "type": "function(str) -> number", - "description": "A function which parses x-values (i.e. the dependent series). Must return a number, even when the values are dates. In this case, millis since epoch are used. This is used primarily for parsing CSV data. *=Dygraphs is slightly more accepting in the dates which it will parse. See code for details." - }, - "stackedGraph": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "If set, stack series on top of one another rather than drawing them independently. The first series specified in the input data will wind up on top of the chart and the last will be on bottom. NaN values are drawn as white areas without a line on top, see stackedGraphNaNFill for details." - }, - "stackedGraphNaNFill": { - "default": "all", - "labels": ["Data Line display"], - "type": "string", - "description": "Controls handling of NaN values inside a stacked graph. NaN values are interpolated/extended for stacking purposes, but the actual point value remains NaN in the legend display. Valid option values are \"all\" (interpolate internally, repeat leftmost and rightmost value as needed), \"inside\" (interpolate internally only, use zero outside leftmost and rightmost value), and \"none\" (treat NaN as zero everywhere)." - }, - "pointSize": { - "default": "1", - "labels": ["Data Line display"], - "type": "integer", - "description": "The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is \"isolated\", i.e. there is a missing point on either side of it. This also controls the size of those dots." - }, - "labelsDivStyles": { - "default": "null", - "labels": ["Legend"], - "type": "{}", - "description": "Additional styles to apply to the currently-highlighted points div. For example, { 'fontWeight': 'bold' } will make the labels bold. In general, it is better to use CSS to style the .dygraph-legend class than to use this property." - }, - "drawPoints": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a drawPointCallback." - }, - "drawGapEdgePoints": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities." - }, - "drawPointCallback": { - "default": "null", - "labels": ["Data Line display"], - "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)", - "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]], - "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy). Also see drawHighlightPointCallback" - }, - "height": { - "default": "320", - "labels": ["Overall display"], - "type": "integer", - "description": "Height, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored." - }, - "zoomCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(minDate, maxDate, yRanges)", - "parameters": [["minDate", "milliseconds since epoch"], ["maxDate", "milliseconds since epoch."], ["yRanges", "is an array of [bottom, top] pairs, one for each y-axis."]], - "description": "A function to call when the zoom window is changed (either by zooming in or out). When animatedZooms is set, zoomCallback is called once at the end of the transition (it will not be called for intermediate frames)." - }, - "pointClickCallback": { - "snippet": "function(e, point){
  alert(point);
}", - "default": "null", - "labels": ["Callbacks", "Interactive Elements"], - "type": "function(e, point)", - "parameters": [["e", "the event object for the click"], ["point", "the point that was clicked See Point properties for details"]], - "description": "A function to call when a data point is clicked. and the point that was clicked." - }, - "color": { - "default": "(see description)", - "labels": ["Data Series Colors"], - "type": "string", - "example": "red", - "description": "A per-series color definition. Used in conjunction with, and overrides, the colors option." - }, - "colors": { - "default": "(see description)", - "labels": ["Data Series Colors"], - "type": "array", - "example": "['red', '#00FF00']", - "description": "List of colors for the data series. These can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\", etc. If not specified, equally-spaced points around a color wheel are used. Overridden by the 'color' option." - }, - "connectSeparatedPoints": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "Usually, when Dygraphs encounters a missing value in a data series, it interprets this as a gap and draws it as such. If, instead, the missing values represents an x-value for which only a different series has data, then you'll want to connect the dots by setting this to true. To explicitly include a gap with this option set, use a value of NaN." - }, - "highlightCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(event, x, points, row, seriesName)", - "description": "When set, this callback gets called every time a new point is highlighted.", - "parameters": [["event", "the JavaScript mousemove event"], ["x", "the x-coordinate of the highlighted points"], ["points", "an array of highlighted points: [ {name: 'series', yval: y-value}, … ]"], ["row", "integer index of the highlighted row in the data table, starting from 0"], ["seriesName", "name of the highlighted series, only present if highlightSeriesOpts is set."]] - }, - "drawHighlightPointCallback": { - "default": "null", - "labels": ["Data Line display"], - "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)", - "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]], - "description": "Draw a custom item when a point is highlighted. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see drawPointCallback" - }, - "highlightSeriesOpts": { - "default": "null", - "labels": ["Interactive Elements"], - "type": "Object", - "description": "When set, the options from this object are applied to the timeseries closest to the mouse pointer for interactive highlighting. See also 'highlightCallback'. Example: highlightSeriesOpts: { strokeWidth: 3 }." - }, - "highlightSeriesBackgroundAlpha": { - "default": "0.5", - "labels": ["Interactive Elements"], - "type": "float", - "description": "Fade the background while highlighting series. 1=fully visible background (disable fading), 0=hiddden background (show highlighted series only)." - }, - "highlightSeriesBackgroundColor": { - "default": "rgb(255, 255, 255)", - "labels": ["Interactive Elements"], - "type": "string", - "description": "Sets the background color used to fade out the series in conjunction with 'highlightSeriesBackgroundAlpha'." - }, - "includeZero": { - "default": "false", - "labels": ["Axis display"], - "type": "boolean", - "description": "Usually, dygraphs will use the range of the data plus some padding to set the range of the y-axis. If this option is set, the y-axis will always include zero, typically as the lowest value. This can be used to avoid exaggerating the variance in the data" - }, - "rollPeriod": { - "default": "1", - "labels": ["Error Bars", "Rolling Averages"], - "type": "integer >= 1", - "description": "Number of days over which to average data. Discussed extensively above." - }, - "unhighlightCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(event)", - "parameters": [["event", "the mouse event"]], - "description": "When set, this callback gets called every time the user stops highlighting any point by mousing out of the graph." - }, - "axisTickSize": { - "default": "3.0", - "labels": ["Axis display"], - "type": "number", - "description": "The size of the line to display next to each tick mark on x- or y-axes." - }, - "labelsSeparateLines": { - "default": "false", - "labels": ["Legend"], - "type": "boolean", - "description": "Put <br/> between lines in the label string. Often used in conjunction with labelsDiv." - }, - "valueFormatter": { - "default": "Depends on the type of your data.", - "labels": ["Legend", "Value display/formatting"], - "type": "function(num or millis, opts, seriesName, dygraph, row, col)", - "description": "Function to provide a custom display format for the values displayed on mouseover. This does not affect the values that appear on tick marks next to the axes. To format those, see axisLabelFormatter. This is usually set on a per-axis basis. .", - "parameters": [["num_or_millis", "The value to be formatted. This is always a number. For date axes, it's millis since epoch. You can call new Date(millis) to get a Date object."], ["opts", "This is a function you can call to access various options (e.g. opts('labelsKMB')). It returns per-axis values for the option when available."], ["seriesName", "The name of the series from which the point came, e.g. 'X', 'Y', 'A', etc."], ["dygraph", "The dygraph object for which the formatting is being done"], ["row", "The row of the data from which this point comes. g.getValue(row, 0) will return the x-value for this point."], ["col", "The column of the data from which this point comes. g.getValue(row, col) will return the original y-value for this point. This can be used to get the full confidence interval for the point, or access un-rolled values for the point."]] - }, - "annotationMouseOverHandler": { - "default": "null", - "labels": ["Annotations"], - "type": "function(annotation, point, dygraph, event)", - "description": "If provided, this function is called whenever the user mouses over an annotation." - }, - "annotationMouseOutHandler": { - "default": "null", - "labels": ["Annotations"], - "type": "function(annotation, point, dygraph, event)", - "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], - "description": "If provided, this function is called whenever the user mouses out of an annotation." - }, - "annotationClickHandler": { - "default": "null", - "labels": ["Annotations"], - "type": "function(annotation, point, dygraph, event)", - "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], - "description": "If provided, this function is called whenever the user clicks on an annotation." - }, - "annotationDblClickHandler": { - "default": "null", - "labels": ["Annotations"], - "type": "function(annotation, point, dygraph, event)", - "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], - "description": "If provided, this function is called whenever the user double-clicks on an annotation." - }, - "drawCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(dygraph, is_initial)", - "parameters": [["dygraph", "The graph being drawn"], ["is_initial", "True if this is the initial draw, false for subsequent draws."]], - "description": "When set, this callback gets called every time the dygraph is drawn. This includes the initial draw, after zooming and repeatedly while panning." - }, - "labelsKMG2": { - "default": "false", - "labels": ["Value display/formatting"], - "type": "boolean", - "description": "Show k/M/G for kilo/Mega/Giga on y-axis. This is different than labelsKMB in that it uses base 2, not 10." - }, - "delimiter": { - "default": ",", - "labels": ["CSV parsing"], - "type": "string", - "description": "The delimiter to look for when separating fields of a CSV file. Setting this to a tab is not usually necessary, since tab-delimited data is auto-detected." - }, - "axisLabelFontSize": { - "default": "14", - "labels": ["Axis display"], - "type": "integer", - "description": "Size of the font (in pixels) to use in the axis labels, both x- and y-axis." - }, - "underlayCallback": { - "default": "null", - "labels": ["Callbacks"], - "type": "function(context, area, dygraph)", - "parameters": [["context", "the canvas drawing context on which to draw"], ["area", "An object with {x,y,w,h} properties describing the drawing area."], ["dygraph", "the reference graph"]], - "description": "When set, this callback gets called before the chart is drawn. It details on how to use this." - }, - "width": { - "default": "480", - "labels": ["Overall display"], - "type": "integer", - "description": "Width, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored." - }, - "interactionModel": { - "default": "...", - "labels": ["Interactive Elements"], - "type": "Object", - "description": "TODO(konigsberg): document this" - }, - "ticker": { - "default": "Dygraph.dateTicker or Dygraph.numericTicks", - "labels": ["Axis display"], - "type": "function(min, max, pixels, opts, dygraph, vals) -> [{v: ..., label: ...}, ...]", - "parameters": [["min", ""], ["max", ""], ["pixels", ""], ["opts", ""], ["dygraph", "the reference graph"], ["vals", ""]], - "description": "This lets you specify an arbitrary function to generate tick marks on an axis. The tick marks are an array of (value, label) pairs. The built-in functions go to great lengths to choose good tick marks so, if you set this option, you'll most likely want to call one of them and modify the result. See dygraph-tickers.js for an extensive discussion. This is set on a per-axis basis." - }, - "xAxisHeight": { - "default": "(null)", - "labels": ["Axis display"], - "type": "integer", - "description": "Height, in pixels, of the x-axis. If not set explicitly, this is computed based on axisLabelFontSize and axisTickSize." - }, - "showLabelsOnHighlight": { - "default": "true", - "labels": ["Interactive Elements", "Legend"], - "type": "boolean", - "description": "Whether to show the legend upon mouseover." - }, - "axis": { - "default": "(none)", - "labels": ["Axis display"], - "type": "string", - "description": "Set to either 'y1' or 'y2' to assign a series to a y-axis (primary or secondary). Must be set per-series." - }, - "pixelsPerLabel": { - "default": "70 (x-axis) or 30 (y-axes)", - "labels": ["Axis display", "Grid"], - "type": "integer", - "description": "Number of pixels to require between each x- and y-label. Larger values will yield a sparser axis with fewer ticks. This is set on a per-axis basis." - }, - "labelsDiv": { - "default": "null", - "labels": ["Legend"], - "type": "DOM element or string", - "example": "document.getElementById('foo')or'foo'", - "description": "Show data labels in an external div, rather than on the graph. This value can either be a div element or a div id." - }, - "fractions": { - "default": "false", - "labels": ["CSV parsing", "Error Bars"], - "type": "boolean", - "description": "When set, attempt to parse each cell in the CSV file as \"a/b\", where a and b are integers. The ratio will be plotted. This allows computation of Wilson confidence intervals (see below)." - }, - "logscale": { - "default": "false", - "labels": ["Axis display"], - "type": "boolean", - "description": "When set for the y-axis or x-axis, the graph shows that axis in log scale. Any values less than or equal to zero are not displayed. Showing log scale with ranges that go below zero will result in an unviewable graph.\n\n Not compatible with showZero. connectSeparatedPoints is ignored. This is ignored for date-based x-axes." - }, - "strokeWidth": { - "default": "1.0", - "labels": ["Data Line display"], - "type": "float", - "example": "0.5, 2.0", - "description": "The width of the lines connecting data points. This can be used to increase the contrast or some graphs." - }, - "strokePattern": { - "default": "null", - "labels": ["Data Line display"], - "type": "array", - "example": "[10, 2, 5, 2]", - "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed lines." - }, - "strokeBorderWidth": { - "default": "null", - "labels": ["Data Line display"], - "type": "float", - "example": "1.0", - "description": "Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines." - }, - "strokeBorderColor": { - "default": "white", - "labels": ["Data Line display"], - "type": "string", - "example": "red, #ccffdd", - "description": "Color for the line border used if strokeBorderWidth is set." - }, - "wilsonInterval": { - "default": "true", - "labels": ["Error Bars"], - "type": "boolean", - "description": "Use in conjunction with the \"fractions\" option. Instead of plotting +/- N standard deviations, dygraphs will compute a Wilson confidence interval and plot that. This has more reasonable behavior for ratios close to 0 or 1." - }, - "fillGraph": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "Should the area underneath the graph be filled? This option is not compatible with error bars. This may be set on a per-series basis." - }, - "highlightCircleSize": { - "default": "3", - "labels": ["Interactive Elements"], - "type": "integer", - "description": "The size in pixels of the dot drawn over highlighted points." - }, - "gridLineColor": { - "default": "rgb(128,128,128)", - "labels": ["Grid"], - "type": "red, blue", - "description": "The color of the gridlines. This may be set on a per-axis basis to define each axis' grid separately." - }, - "gridLinePattern": { - "default": "null", - "labels": ["Grid"], - "type": "array", - "example": "[10, 2, 5, 2]", - "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed gridlines." - }, - "visibility": { - "default": "[true, true, ...]", - "labels": ["Data Line display"], - "type": "Array of booleans", - "description": "Which series should initially be visible? Once the Dygraph has been constructed, you can access and modify the visibility of each series using the visibility and setVisibility methods." - }, - "valueRange": { - "default": "Full range of the input is shown", - "labels": ["Axis display"], - "type": "Array of two numbers", - "example": "[10, 110]", - "description": "Explicitly set the vertical range of the graph to [low, high]. This may be set on a per-axis basis to define each y-axis separately. If either limit is unspecified, it will be calculated automatically (e.g. [null, 30] to automatically calculate just the lower bound)" - }, - "labelsDivWidth": { - "default": "250", - "labels": ["Legend"], - "type": "integer", - "description": "Width (in pixels) of the div which shows information on the currently-highlighted points." - }, - "colorSaturation": { - "default": "1.0", - "labels": ["Data Series Colors"], - "type": "float (0.0 - 1.0)", - "description": "If colors is not specified, saturation of the automatically-generated data series colors." - }, - "hideOverlayOnMouseOut": { - "default": "true", - "labels": ["Interactive Elements", "Legend"], - "type": "boolean", - "description": "Whether to hide the legend when the mouse leaves the chart area." - }, - "legend": { - "default": "onmouseover", - "labels": ["Legend"], - "type": "string", - "description": "When to display the legend. By default, it only appears when a user mouses over the chart. Set it to \"always\" to always display a legend of some sort. When set to \"follow\", legend follows highlighted points." - }, - "legendFormatter": { - "default": "null", - "labels": ["Legend"], - "type": "function(data): string", - "params": [["data", "An object containing information about the selection (or lack of a selection). This includes formatted values and series information. See here for sample values."]], - "description": "Set this to supply a custom formatter for the legend. See this comment and the legendFormatter demo for usage." - }, - "labelsShowZeroValues": { - "default": "true", - "labels": ["Legend"], - "type": "boolean", - "description": "Show zero value labels in the labelsDiv." - }, - "stepPlot": { - "default": "false", - "labels": ["Data Line display"], - "type": "boolean", - "description": "When set, display the graph as a step plot instead of a line plot. This option may either be set for the whole graph or for single series." - }, - "labelsUTC": { - "default": "false", - "labels": ["Value display/formatting", "Axis display"], - "type": "boolean", - "description": "Show date/time labels according to UTC (instead of local time)." - }, - "labelsKMB": { - "default": "false", - "labels": ["Value display/formatting"], - "type": "boolean", - "description": "Show K/M/B for thousands/millions/billions on y-axis." - }, - "rightGap": { - "default": "5", - "labels": ["Overall display"], - "type": "integer", - "description": "Number of pixels to leave blank at the right edge of the Dygraph. This makes it easier to highlight the right-most data point." - }, - "avoidMinZero": { - "default": "false", - "labels": ["Deprecated"], - "type": "boolean", - "description": "Deprecated, please use yRangePad instead. When set, the heuristic that fixes the Y axis at zero for a data set with the minimum Y value of zero is disabled. \nThis is particularly useful for data sets that contain many zero values, especially for step plots which may otherwise have lines not visible running along the bottom axis." - }, - "drawAxesAtZero": { - "default": "false", - "labels": ["Axis display"], - "type": "boolean", - "description": "When set, draw the X axis at the Y=0 position and the Y axis at the X=0 position if those positions are inside the graph's visible area. Otherwise, draw the axes at the bottom or left graph edge as usual." - }, - "xRangePad": { - "default": "0", - "labels": ["Axis display"], - "type": "float", - "description": "Add the specified amount of extra space (in pixels) around the X-axis value range to ensure points at the edges remain visible." - }, - "yRangePad": { - "default": "null", - "labels": ["Axis display"], - "type": "float", - "description": "If set, add the specified amount of extra space (in pixels) around the Y-axis value range to ensure points at the edges remain visible. If unset, use the traditional Y padding algorithm." - }, - "axisLabelFormatter": { - "default": "Depends on the data type", - "labels": ["Axis display"], - "type": "function(number or Date, granularity, opts, dygraph)", - "parameters": [["number or date", "Either a number (for a numeric axis) or a Date object (for a date axis)"], ["granularity", "specifies how fine-grained the axis is. For date axes, this is a reference to the time granularity enumeration, defined in dygraph-tickers.js, e.g. Dygraph.WEEKLY."], ["opts", "a function which provides access to various options on the dygraph, e.g. opts('labelsKMB')."], ["dygraph", "the referenced graph"]], - "description": "Function to call to format the tick values that appear along an axis. This is usually set on a per-axis basis." - }, - "clickCallback": { - "snippet": "function(e, date_millis){
  alert(new Date(date_millis));
}", - "default": "null", - "labels": ["Callbacks"], - "type": "function(e, x, points)", - "parameters": [["e", "The event object for the click"], ["x", "The x value that was clicked (for dates, this is milliseconds since epoch)"], ["points", "The closest points along that date. See Point properties for details."]], - "description": "A function to call when the canvas is clicked." - }, - "labels": { - "default": "[\"X\", \"Y1\", \"Y2\", ...]*", - "labels": ["Legend"], - "type": "array", - "description": "A name for each data series, including the independent (X) series. For CSV files and DataTable objections, this is determined by context. For raw data, this must be specified. If it is not, default values are supplied and a warning is logged." - }, - "dateWindow": { - "default": "Full range of the input is shown", - "labels": ["Axis display"], - "type": "Array of two numbers", - "example": "[
  Date.parse('2006-01-01'),
  (new Date()).valueOf()
]", - "description": "Initially zoom in on a section of the graph. Is of the form [earliest, latest], where earliest/latest are milliseconds since epoch. If the data for the x-axis is numeric, the values in dateWindow must also be numbers." - }, - "showRoller": { - "default": "false", - "labels": ["Interactive Elements", "Rolling Averages"], - "type": "boolean", - "description": "If the rolling average period text box should be shown." - }, - "sigma": { - "default": "2.0", - "labels": ["Error Bars"], - "type": "float", - "description": "When errorBars is set, shade this many standard deviations above/below each point." - }, - "customBars": { - "default": "false", - "labels": ["CSV parsing", "Error Bars"], - "type": "boolean", - "description": "When set, parse each CSV cell as \"low;middle;high\". Error bars will be drawn for each point between low and high, with the series itself going through middle." - }, - "colorValue": { - "default": "1.0", - "labels": ["Data Series Colors"], - "type": "float (0.0 - 1.0)", - "description": "If colors is not specified, value of the data series colors, as in hue/saturation/value. (0.0-1.0, default 0.5)" - }, - "errorBars": { - "default": "false", - "labels": ["CSV parsing", "Error Bars"], - "type": "boolean", - "description": "Does the data contain standard deviations? Setting this to true alters the input format (see above)." - }, - "displayAnnotations": { - "default": "false", - "labels": ["Annotations"], - "type": "boolean", - "description": "Only applies when Dygraphs is used as a GViz chart. Causes string columns following a data series to be interpreted as annotations on points in that series. This is the same format used by Google's AnnotatedTimeLine chart." - }, - "panEdgeFraction": { - "default": "null", - "labels": ["Axis display", "Interactive Elements"], - "type": "float", - "description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% pased the edges of the displayed values. null means no bounds." - }, - "title": { - "labels": ["Chart labels"], - "type": "string", - "default": "null", - "description": "Text to display above the chart. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-title' classes." - }, - "titleHeight": { - "default": "18", - "labels": ["Chart labels"], - "type": "integer", - "description": "Height of the chart title, in pixels. This also controls the default font size of the title. If you style the title on your own, this controls how much space is set aside above the chart for the title's div." - }, - "xlabel": { - "labels": ["Chart labels"], - "type": "string", - "default": "null", - "description": "Text to display below the chart's x-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-xlabel' classes." - }, - "xLabelHeight": { - "labels": ["Chart labels"], - "type": "integer", - "default": "18", - "description": "Height of the x-axis label, in pixels. This also controls the default font size of the x-axis label. If you style the label on your own, this controls how much space is set aside below the chart for the x-axis label's div." - }, - "ylabel": { - "labels": ["Chart labels"], - "type": "string", - "default": "null", - "description": "Text to display to the left of the chart's y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-ylabel' classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the yAxisLabelWidth option. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it's rotated, so width is controlled by the 'height' property) or set the yLabelWidth option." - }, - "y2label": { - "labels": ["Chart labels"], - "type": "string", - "default": "null", - "description": "Text to display to the right of the chart's secondary y-axis. This label is only displayed if a secondary y-axis is present. See this test for an example of how to do this. The comments for the 'ylabel' option generally apply here as well. This label gets a 'dygraph-y2label' instead of a 'dygraph-ylabel' class." - }, - "yLabelWidth": { - "labels": ["Chart labels"], - "type": "integer", - "default": "18", - "description": "Width of the div which contains the y-axis label. Since the y-axis label appears rotated 90 degrees, this actually affects the height of its div." - }, - "isZoomedIgnoreProgrammaticZoom": { - "default": "false", - "labels": ["Zooming"], - "type": "boolean", - "description": "When this option is passed to updateOptions() along with either the dateWindow or valueRange options, the zoom flags are not changed to reflect a zoomed state. This is primarily useful for when the display area of a chart is changed programmatically and also where manual zooming is allowed and use is made of the isZoomed method to determine this." - }, - "drawGrid": { - "default": "true for x and y, false for y2", - "labels": ["Grid"], - "type": "boolean", - "description": "Whether to display gridlines in the chart. This may be set on a per-axis basis to define the visibility of each axis' grid separately." - }, - "independentTicks": { - "default": "true for y, false for y2", - "labels": ["Axis display", "Grid"], - "type": "boolean", - "description": "Only valid for y and y2, has no effect on x: This option defines whether the y axes should align their ticks or if they should be independent. Possible combinations: 1.) y=true, y2=false (default): y is the primary axis and the y2 ticks are aligned to the the ones of y. (only 1 grid) 2.) y=false, y2=true: y2 is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 grid) 3.) y=true, y2=true: Both axis are independent and have their own ticks. (2 grids) 4.) y=false, y2=false: Invalid configuration causes an error." - }, - "drawAxis": { - "default": "true for x and y, false for y2", - "labels": ["Axis display"], - "type": "boolean", - "description": "Whether to draw the specified axis. This may be set on a per-axis basis to define the visibility of each axis separately. Setting this to false also prevents axis ticks from being drawn and reclaims the space for the chart grid/lines." - }, - "gridLineWidth": { - "default": "0.3", - "labels": ["Grid"], - "type": "float", - "description": "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawGrid option. This may be set on a per-axis basis to define each axis' grid separately." - }, - "axisLineWidth": { - "default": "0.3", - "labels": ["Axis display"], - "type": "float", - "description": "Thickness (in pixels) of the x- and y-axis lines." - }, - "axisLineColor": { - "default": "black", - "labels": ["Axis display"], - "type": "string", - "description": "Color of the x- and y-axis lines. Accepts any value which the HTML canvas strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'." - }, - "fillAlpha": { - "default": "0.15", - "labels": ["Error Bars", "Data Series Colors"], - "type": "float (0.0 - 1.0)", - "description": "Error bars (or custom bars) for each series are drawn in the same color as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the error bars will not be drawn, whereas a value of 1.0 means that the error bars will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point." - }, - "axisLabelColor": { - "default": "black", - "labels": ["Axis display"], - "type": "string", - "description": "Color for x- and y-axis labels. This is a CSS color string." - }, - "axisLabelWidth": { - "default": "50 (y-axis), 60 (x-axis)", - "labels": ["Axis display", "Chart labels"], - "type": "integer", - "description": "Width (in pixels) of the containing divs for x- and y-axis labels. For the y-axis, this also controls the width of the y-axis. Note that for the x-axis, this is independent from pixelsPerLabel, which controls the spacing between labels." - }, - "sigFigs": { - "default": "null", - "labels": ["Value display/formatting"], - "type": "integer", - "description": "By default, dygraphs displays numbers with a fixed number of digits after the decimal point. If you'd prefer to have a fixed number of significant figures, set this option to that number of sig figs. A value of 2, for instance, would cause 1 to be display as 1.0 and 1234 to be displayed as 1.23e+3." - }, - "digitsAfterDecimal": { - "default": "2", - "labels": ["Value display/formatting"], - "type": "integer", - "description": "Unless it's run in scientific mode (see the sigFigs option), dygraphs displays numbers with digitsAfterDecimal digits after the decimal point. Trailing zeros are not displayed, so with a value of 2 you'll get '0', '0.1', '0.12', '123.45' but not '123.456' (it will be rounded to '123.46'). Numbers with absolute value less than 0.1^digitsAfterDecimal (i.e. those which would show up as '0.00') will be displayed in scientific notation." - }, - "maxNumberWidth": { - "default": "6", - "labels": ["Value display/formatting"], - "type": "integer", - "description": "When displaying numbers in normal (not scientific) mode, large numbers will be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This can lead to unwieldy y-axis labels. If there are more than maxNumberWidth digits to the left of the decimal in a number, dygraphs will switch to scientific notation, even when not operating in scientific mode. If you'd like to see all those digits, set this to something large, like 20 or 30." - }, - "file": { - "default": "(set when constructed)", - "labels": ["Data"], - "type": "string (URL of CSV or CSV), GViz DataTable or 2D Array", - "description": "Sets the data being displayed in the chart. This can only be set when calling updateOptions; it cannot be set from the constructor. For a full description of valid data formats, see the Data Formats page." - }, - "timingName": { - "default": "null", - "labels": ["Debugging"], - "type": "string", - "description": "Set this option to log timing information. The value of the option will be logged along with the timimg, so that you can distinguish multiple dygraphs on the same page." - }, - "showRangeSelector": { - "default": "false", - "labels": ["Range Selector"], - "type": "boolean", - "description": "Show or hide the range selector widget." - }, - "rangeSelectorHeight": { - "default": "40", - "labels": ["Range Selector"], - "type": "integer", - "description": "Height, in pixels, of the range selector widget. This option can only be specified at Dygraph creation time." - }, - "rangeSelectorPlotStrokeColor": { - "default": "#808FAB", - "labels": ["Range Selector"], - "type": "string", - "description": "The range selector mini plot stroke color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off stroke." - }, - "rangeSelectorPlotFillColor": { - "default": "#A7B1C4", - "labels": ["Range Selector"], - "type": "string", - "description": "The range selector mini plot fill color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off fill." - }, - "rangeSelectorPlotFillGradientColor": { - "default": "white", - "labels": ["Range Selector"], - "type": "string", - "description": "The top color for the range selector mini plot fill color gradient. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"rgba(255,100,200,42)\" or \"yellow\". You can also specify null or \"\" to disable the gradient and fill with one single color." - }, - "rangeSelectorBackgroundStrokeColor": { - "default": "gray", - "labels": ["Range Selector"], - "type": "string", - "description": "The color of the lines below and on both sides of the range selector mini plot. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"." - }, - "rangeSelectorBackgroundLineWidth": { - "default": "1", - "labels": ["Range Selector"], - "type": "float", - "description": "The width of the lines below and on both sides of the range selector mini plot." - }, - "rangeSelectorPlotLineWidth": { - "default": "1.5", - "labels": ["Range Selector"], - "type": "float", - "description": "The width of the range selector mini plot line." - }, - "rangeSelectorForegroundStrokeColor": { - "default": "black", - "labels": ["Range Selector"], - "type": "string", - "description": "The color of the lines in the interactive layer of the range selector. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"." - }, - "rangeSelectorForegroundLineWidth": { - "default": "1", - "labels": ["Range Selector"], - "type": "float", - "description": "The width the lines in the interactive layer of the range selector." - }, - "rangeSelectorAlpha": { - "default": "0.6", - "labels": ["Range Selector"], - "type": "float (0.0 - 1.0)", - "description": "The transparency of the veil that is drawn over the unselected portions of the range selector mini plot. A value of 0 represents full transparency and the unselected portions of the mini plot will appear as normal. A value of 1 represents full opacity and the unselected portions of the mini plot will be hidden." - }, - "showInRangeSelector": { - "default": "null", - "labels": ["Range Selector"], - "type": "boolean", - "description": "Mark this series for inclusion in the range selector. The mini plot curve will be an average of all such series. If this is not specified for any series, the default behavior is to average all the series. Setting it for one series will result in that series being charted alone in the range selector." - }, - "animatedZooms": { - "default": "false", - "labels": ["Interactive Elements"], - "type": "boolean", - "description": "Set this option to animate the transition between zoom windows. Applies to programmatic and interactive zooms. Note that if you also set a drawCallback, it will be called several times on each zoom. If you set a zoomCallback, it will only be called after the animation is complete." - }, - "plotter": { - "default": "[DygraphCanvasRenderer.Plotters.fillPlotter, DygraphCanvasRenderer.Plotters.errorPlotter, DygraphCanvasRenderer.Plotters.linePlotter]", - "labels": ["Data Line display"], - "type": "array or function", - "description": "A function (or array of functions) which plot each data series on the chart. TODO(danvk): more details! May be set per-series." - }, - "axes": { - "default": "null", - "labels": ["Configuration"], - "type": "Object", - "description": "Defines per-axis options. Valid keys are 'x', 'y' and 'y2'. Only some options may be set on a per-axis basis. If an option may be set in this way, it will be noted on this page. See also documentation on per-series and per-axis options." - }, - "series": { - "default": "null", - "labels": ["Series"], - "type": "Object", - "description": "Defines per-series options. Its keys match the y-axis label names, and the values are dictionaries themselves that contain options specific to that series." - }, - "plugins": { - "default": "[]", - "labels": ["Configuration"], - "type": "Array", - "description": "Defines per-graph plugins. Useful for per-graph customization" - }, - "dataHandler": { - "default": "(depends on data)", - "labels": ["Data"], - "type": "Dygraph.DataHandler", - "description": "Custom DataHandler. This is an advanced customization. See http://bit.ly/151E7Aq." - } - }; //
- // NOTE: in addition to parsing as JS, this snippet is expected to be valid - // JSON. This assumption cannot be checked in JS, but it will be checked when - // documentation is generated by the generate-documentation.py script. For the - // most part, this just means that you should always use double quotes. - - // Do a quick sanity check on the options reference. - var warn = function warn(msg) { - if (window.console) window.console.warn(msg); - }; - var flds = ['type', 'default', 'description']; - var valid_cats = ['Annotations', 'Axis display', 'Chart labels', 'CSV parsing', 'Callbacks', 'Data', 'Data Line display', 'Data Series Colors', 'Error Bars', 'Grid', 'Interactive Elements', 'Range Selector', 'Legend', 'Overall display', 'Rolling Averages', 'Series', 'Value display/formatting', 'Zooming', 'Debugging', 'Configuration', 'Deprecated']; - var i; - var cats = {}; - for (i = 0; i < valid_cats.length; i++) cats[valid_cats[i]] = true; - - for (var k in OPTIONS_REFERENCE) { - if (!OPTIONS_REFERENCE.hasOwnProperty(k)) continue; - var op = OPTIONS_REFERENCE[k]; - for (i = 0; i < flds.length; i++) { - if (!op.hasOwnProperty(flds[i])) { - warn('Option ' + k + ' missing "' + flds[i] + '" property'); - } else if (typeof op[flds[i]] != 'string') { - warn(k + '.' + flds[i] + ' must be of type string'); - } - } - var labels = op.labels; - if (typeof labels !== 'object') { - warn('Option "' + k + '" is missing a "labels": [...] option'); - } else { - for (i = 0; i < labels.length; i++) { - if (!cats.hasOwnProperty(labels[i])) { - warn('Option "' + k + '" has label "' + labels[i] + '", which is invalid.'); - } - } - } - } -} - -exports["default"] = OPTIONS_REFERENCE; -module.exports = exports["default"]; - -},{}],14:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview DygraphOptions is responsible for parsing and returning - * information about options. - */ - -// TODO: remove this jshint directive & fix the warnings. -/*jshint sub:true */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -var _dygraphDefaultAttrs = require('./dygraph-default-attrs'); - -var _dygraphDefaultAttrs2 = _interopRequireDefault(_dygraphDefaultAttrs); - -var _dygraphOptionsReference = require('./dygraph-options-reference'); - -var _dygraphOptionsReference2 = _interopRequireDefault(_dygraphOptionsReference); - -/* - * Interesting member variables: (REMOVING THIS LIST AS I CLOSURIZE) - * global_ - global attributes (common among all graphs, AIUI) - * user - attributes set by the user - * series_ - { seriesName -> { idx, yAxis, options }} - */ - -/** - * This parses attributes into an object that can be easily queried. - * - * It doesn't necessarily mean that all options are available, specifically - * if labels are not yet available, since those drive details of the per-series - * and per-axis options. - * - * @param {Dygraph} dygraph The chart to which these options belong. - * @constructor - */ -var DygraphOptions = function DygraphOptions(dygraph) { - /** - * The dygraph. - * @type {!Dygraph} - */ - this.dygraph_ = dygraph; - - /** - * Array of axis index to { series : [ series names ] , options : { axis-specific options. } - * @type {Array.<{series : Array., options : Object}>} @private - */ - this.yAxes_ = []; - - /** - * Contains x-axis specific options, which are stored in the options key. - * This matches the yAxes_ object structure (by being a dictionary with an - * options element) allowing for shared code. - * @type {options: Object} @private - */ - this.xAxis_ = {}; - this.series_ = {}; - - // Once these two objects are initialized, you can call get(); - this.global_ = this.dygraph_.attrs_; - this.user_ = this.dygraph_.user_attrs_ || {}; - - /** - * A list of series in columnar order. - * @type {Array.} - */ - this.labels_ = []; - - this.highlightSeries_ = this.get("highlightSeriesOpts") || {}; - this.reparseSeries(); -}; - -/** - * Not optimal, but does the trick when you're only using two axes. - * If we move to more axes, this can just become a function. - * - * @type {Object.} - * @private - */ -DygraphOptions.AXIS_STRING_MAPPINGS_ = { - 'y': 0, - 'Y': 0, - 'y1': 0, - 'Y1': 0, - 'y2': 1, - 'Y2': 1 -}; - -/** - * @param {string|number} axis - * @private - */ -DygraphOptions.axisToIndex_ = function (axis) { - if (typeof axis == "string") { - if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) { - return DygraphOptions.AXIS_STRING_MAPPINGS_[axis]; - } - throw "Unknown axis : " + axis; - } - if (typeof axis == "number") { - if (axis === 0 || axis === 1) { - return axis; - } - throw "Dygraphs only supports two y-axes, indexed from 0-1."; - } - if (axis) { - throw "Unknown axis : " + axis; - } - // No axis specification means axis 0. - return 0; -}; - -/** - * Reparses options that are all related to series. This typically occurs when - * options are either updated, or source data has been made available. - * - * TODO(konigsberg): The method name is kind of weak; fix. - */ -DygraphOptions.prototype.reparseSeries = function () { - var labels = this.get("labels"); - if (!labels) { - return; // -- can't do more for now, will parse after getting the labels. - } - - this.labels_ = labels.slice(1); - - this.yAxes_ = [{ series: [], options: {} }]; // Always one axis at least. - this.xAxis_ = { options: {} }; - this.series_ = {}; - - // Series are specified in the series element: - // - // { - // labels: [ "X", "foo", "bar" ], - // pointSize: 3, - // series : { - // foo : {}, // options for foo - // bar : {} // options for bar - // } - // } - // - // So, if series is found, it's expected to contain per-series data, otherwise set a - // default. - var seriesDict = this.user_.series || {}; - for (var idx = 0; idx < this.labels_.length; idx++) { - var seriesName = this.labels_[idx]; - var optionsForSeries = seriesDict[seriesName] || {}; - var yAxis = DygraphOptions.axisToIndex_(optionsForSeries["axis"]); - - this.series_[seriesName] = { - idx: idx, - yAxis: yAxis, - options: optionsForSeries }; - - if (!this.yAxes_[yAxis]) { - this.yAxes_[yAxis] = { series: [seriesName], options: {} }; - } else { - this.yAxes_[yAxis].series.push(seriesName); - } - } - - var axis_opts = this.user_["axes"] || {}; - utils.update(this.yAxes_[0].options, axis_opts["y"] || {}); - if (this.yAxes_.length > 1) { - utils.update(this.yAxes_[1].options, axis_opts["y2"] || {}); - } - utils.update(this.xAxis_.options, axis_opts["x"] || {}); - - // For "production" code, this gets removed by uglifyjs. - if ("development" != 'production') { - this.validateOptions_(); - } -}; - -/** - * Get a global value. - * - * @param {string} name the name of the option. - */ -DygraphOptions.prototype.get = function (name) { - var result = this.getGlobalUser_(name); - if (result !== null) { - return result; - } - return this.getGlobalDefault_(name); -}; - -DygraphOptions.prototype.getGlobalUser_ = function (name) { - if (this.user_.hasOwnProperty(name)) { - return this.user_[name]; - } - return null; -}; - -DygraphOptions.prototype.getGlobalDefault_ = function (name) { - if (this.global_.hasOwnProperty(name)) { - return this.global_[name]; - } - if (_dygraphDefaultAttrs2['default'].hasOwnProperty(name)) { - return _dygraphDefaultAttrs2['default'][name]; - } - return null; -}; - -/** - * Get a value for a specific axis. If there is no specific value for the axis, - * the global value is returned. - * - * @param {string} name the name of the option. - * @param {string|number} axis the axis to search. Can be the string representation - * ("y", "y2") or the axis number (0, 1). - */ -DygraphOptions.prototype.getForAxis = function (name, axis) { - var axisIdx; - var axisString; - - // Since axis can be a number or a string, straighten everything out here. - if (typeof axis == 'number') { - axisIdx = axis; - axisString = axisIdx === 0 ? "y" : "y2"; - } else { - if (axis == "y1") { - axis = "y"; - } // Standardize on 'y'. Is this bad? I think so. - if (axis == "y") { - axisIdx = 0; - } else if (axis == "y2") { - axisIdx = 1; - } else if (axis == "x") { - axisIdx = -1; // simply a placeholder for below. - } else { - throw "Unknown axis " + axis; - } - axisString = axis; - } - - var userAxis = axisIdx == -1 ? this.xAxis_ : this.yAxes_[axisIdx]; - - // Search the user-specified axis option first. - if (userAxis) { - // This condition could be removed if we always set up this.yAxes_ for y2. - var axisOptions = userAxis.options; - if (axisOptions.hasOwnProperty(name)) { - return axisOptions[name]; - } - } - - // User-specified global options second. - // But, hack, ignore globally-specified 'logscale' for 'x' axis declaration. - if (!(axis === 'x' && name === 'logscale')) { - var result = this.getGlobalUser_(name); - if (result !== null) { - return result; - } - } - // Default axis options third. - var defaultAxisOptions = _dygraphDefaultAttrs2['default'].axes[axisString]; - if (defaultAxisOptions.hasOwnProperty(name)) { - return defaultAxisOptions[name]; - } - - // Default global options last. - return this.getGlobalDefault_(name); -}; - -/** - * Get a value for a specific series. If there is no specific value for the series, - * the value for the axis is returned (and afterwards, the global value.) - * - * @param {string} name the name of the option. - * @param {string} series the series to search. - */ -DygraphOptions.prototype.getForSeries = function (name, series) { - // Honors indexes as series. - if (series === this.dygraph_.getHighlightSeries()) { - if (this.highlightSeries_.hasOwnProperty(name)) { - return this.highlightSeries_[name]; - } - } - - if (!this.series_.hasOwnProperty(series)) { - throw "Unknown series: " + series; - } - - var seriesObj = this.series_[series]; - var seriesOptions = seriesObj["options"]; - if (seriesOptions.hasOwnProperty(name)) { - return seriesOptions[name]; - } - - return this.getForAxis(name, seriesObj["yAxis"]); -}; - -/** - * Returns the number of y-axes on the chart. - * @return {number} the number of axes. - */ -DygraphOptions.prototype.numAxes = function () { - return this.yAxes_.length; -}; - -/** - * Return the y-axis for a given series, specified by name. - */ -DygraphOptions.prototype.axisForSeries = function (series) { - return this.series_[series].yAxis; -}; - -/** - * Returns the options for the specified axis. - */ -// TODO(konigsberg): this is y-axis specific. Support the x axis. -DygraphOptions.prototype.axisOptions = function (yAxis) { - return this.yAxes_[yAxis].options; -}; - -/** - * Return the series associated with an axis. - */ -DygraphOptions.prototype.seriesForAxis = function (yAxis) { - return this.yAxes_[yAxis].series; -}; - -/** - * Return the list of all series, in their columnar order. - */ -DygraphOptions.prototype.seriesNames = function () { - return this.labels_; -}; - -// For "production" code, this gets removed by uglifyjs. -if ("development" != 'production') { - - /** - * Validate all options. - * This requires OPTIONS_REFERENCE, which is only available in debug builds. - * @private - */ - DygraphOptions.prototype.validateOptions_ = function () { - if (typeof _dygraphOptionsReference2['default'] === 'undefined') { - throw 'Called validateOptions_ in prod build.'; - } - - var that = this; - var validateOption = function validateOption(optionName) { - if (!_dygraphOptionsReference2['default'][optionName]) { - that.warnInvalidOption_(optionName); - } - }; - - var optionsDicts = [this.xAxis_.options, this.yAxes_[0].options, this.yAxes_[1] && this.yAxes_[1].options, this.global_, this.user_, this.highlightSeries_]; - var names = this.seriesNames(); - for (var i = 0; i < names.length; i++) { - var name = names[i]; - if (this.series_.hasOwnProperty(name)) { - optionsDicts.push(this.series_[name].options); - } - } - for (var i = 0; i < optionsDicts.length; i++) { - var dict = optionsDicts[i]; - if (!dict) continue; - for (var optionName in dict) { - if (dict.hasOwnProperty(optionName)) { - validateOption(optionName); - } - } - } - }; - - var WARNINGS = {}; // Only show any particular warning once. - - /** - * Logs a warning about invalid options. - * TODO: make this throw for testing - * @private - */ - DygraphOptions.prototype.warnInvalidOption_ = function (optionName) { - if (!WARNINGS[optionName]) { - WARNINGS[optionName] = true; - var isSeries = this.labels_.indexOf(optionName) >= 0; - if (isSeries) { - console.warn('Use new-style per-series options (saw ' + optionName + ' as top-level options key). See http://bit.ly/1tceaJs'); - } else { - console.warn('Unknown option ' + optionName + ' (full list of options at dygraphs.com/options.html'); - throw "invalid option " + optionName; - } - } - }; - - // Reset list of previously-shown warnings. Used for testing. - DygraphOptions.resetWarnings_ = function () { - WARNINGS = {}; - }; -} - -exports['default'] = DygraphOptions; -module.exports = exports['default']; - -},{"./dygraph-default-attrs":9,"./dygraph-options-reference":13,"./dygraph-utils":16}],15:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview Description of this file. - * @author danvk@google.com (Dan Vanderkam) - * - * A ticker is a function with the following interface: - * - * function(a, b, pixels, options_view, dygraph, forced_values); - * -> [ { v: tick1_v, label: tick1_label[, label_v: label_v1] }, - * { v: tick2_v, label: tick2_label[, label_v: label_v2] }, - * ... - * ] - * - * The returned value is called a "tick list". - * - * Arguments - * --------- - * - * [a, b] is the range of the axis for which ticks are being generated. For a - * numeric axis, these will simply be numbers. For a date axis, these will be - * millis since epoch (convertable to Date objects using "new Date(a)" and "new - * Date(b)"). - * - * opts provides access to chart- and axis-specific options. It can be used to - * access number/date formatting code/options, check for a log scale, etc. - * - * pixels is the length of the axis in pixels. opts('pixelsPerLabel') is the - * minimum amount of space to be allotted to each label. For instance, if - * pixels=400 and opts('pixelsPerLabel')=40 then the ticker should return - * between zero and ten (400/40) ticks. - * - * dygraph is the Dygraph object for which an axis is being constructed. - * - * forced_values is used for secondary y-axes. The tick positions are typically - * set by the primary y-axis, so the secondary y-axis has no choice in where to - * put these. It simply has to generate labels for these data values. - * - * Tick lists - * ---------- - * Typically a tick will have both a grid/tick line and a label at one end of - * that line (at the bottom for an x-axis, at left or right for the y-axis). - * - * A tick may be missing one of these two components: - * - If "label_v" is specified instead of "v", then there will be no tick or - * gridline, just a label. - * - Similarly, if "label" is not specified, then there will be a gridline - * without a label. - * - * This flexibility is useful in a few situations: - * - For log scales, some of the tick lines may be too close to all have labels. - * - For date scales where years are being displayed, it is desirable to display - * tick marks at the beginnings of years but labels (e.g. "2006") in the - * middle of the years. - */ - -/*jshint sub:true */ -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('./dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** @typedef {Array.<{v:number, label:string, label_v:(string|undefined)}>} */ -var TickList = undefined; // the ' = undefined' keeps jshint happy. - -/** @typedef {function( - * number, - * number, - * number, - * function(string):*, - * Dygraph=, - * Array.= - * ): TickList} - */ -var Ticker = undefined; // the ' = undefined' keeps jshint happy. - -/** @type {Ticker} */ -var numericLinearTicks = function numericLinearTicks(a, b, pixels, opts, dygraph, vals) { - var nonLogscaleOpts = function nonLogscaleOpts(opt) { - if (opt === 'logscale') return false; - return opts(opt); - }; - return numericTicks(a, b, pixels, nonLogscaleOpts, dygraph, vals); -}; - -exports.numericLinearTicks = numericLinearTicks; -/** @type {Ticker} */ -var numericTicks = function numericTicks(a, b, pixels, opts, dygraph, vals) { - var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel'); - var ticks = []; - var i, j, tickV, nTicks; - if (vals) { - for (i = 0; i < vals.length; i++) { - ticks.push({ v: vals[i] }); - } - } else { - // TODO(danvk): factor this log-scale block out into a separate function. - if (opts("logscale")) { - nTicks = Math.floor(pixels / pixels_per_tick); - var minIdx = utils.binarySearch(a, PREFERRED_LOG_TICK_VALUES, 1); - var maxIdx = utils.binarySearch(b, PREFERRED_LOG_TICK_VALUES, -1); - if (minIdx == -1) { - minIdx = 0; - } - if (maxIdx == -1) { - maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1; - } - // Count the number of tick values would appear, if we can get at least - // nTicks / 4 accept them. - var lastDisplayed = null; - if (maxIdx - minIdx >= nTicks / 4) { - for (var idx = maxIdx; idx >= minIdx; idx--) { - var tickValue = PREFERRED_LOG_TICK_VALUES[idx]; - var pixel_coord = Math.log(tickValue / a) / Math.log(b / a) * pixels; - var tick = { v: tickValue }; - if (lastDisplayed === null) { - lastDisplayed = { - tickValue: tickValue, - pixel_coord: pixel_coord - }; - } else { - if (Math.abs(pixel_coord - lastDisplayed.pixel_coord) >= pixels_per_tick) { - lastDisplayed = { - tickValue: tickValue, - pixel_coord: pixel_coord - }; - } else { - tick.label = ""; - } - } - ticks.push(tick); - } - // Since we went in backwards order. - ticks.reverse(); - } - } - - // ticks.length won't be 0 if the log scale function finds values to insert. - if (ticks.length === 0) { - // Basic idea: - // Try labels every 1, 2, 5, 10, 20, 50, 100, etc. - // Calculate the resulting tick spacing (i.e. this.height_ / nTicks). - // The first spacing greater than pixelsPerYLabel is what we use. - // TODO(danvk): version that works on a log scale. - var kmg2 = opts("labelsKMG2"); - var mults, base; - if (kmg2) { - mults = [1, 2, 4, 8, 16, 32, 64, 128, 256]; - base = 16; - } else { - mults = [1, 2, 5, 10, 20, 50, 100]; - base = 10; - } - - // Get the maximum number of permitted ticks based on the - // graph's pixel size and pixels_per_tick setting. - var max_ticks = Math.ceil(pixels / pixels_per_tick); - - // Now calculate the data unit equivalent of this tick spacing. - // Use abs() since graphs may have a reversed Y axis. - var units_per_tick = Math.abs(b - a) / max_ticks; - - // Based on this, get a starting scale which is the largest - // integer power of the chosen base (10 or 16) that still remains - // below the requested pixels_per_tick spacing. - var base_power = Math.floor(Math.log(units_per_tick) / Math.log(base)); - var base_scale = Math.pow(base, base_power); - - // Now try multiples of the starting scale until we find one - // that results in tick marks spaced sufficiently far apart. - // The "mults" array should cover the range 1 .. base^2 to - // adjust for rounding and edge effects. - var scale, low_val, high_val, spacing; - for (j = 0; j < mults.length; j++) { - scale = base_scale * mults[j]; - low_val = Math.floor(a / scale) * scale; - high_val = Math.ceil(b / scale) * scale; - nTicks = Math.abs(high_val - low_val) / scale; - spacing = pixels / nTicks; - if (spacing > pixels_per_tick) break; - } - - // Construct the set of ticks. - // Allow reverse y-axis if it's explicitly requested. - if (low_val > high_val) scale *= -1; - for (i = 0; i <= nTicks; i++) { - tickV = low_val + i * scale; - ticks.push({ v: tickV }); - } - } - } - - var formatter = /**@type{AxisLabelFormatter}*/opts('axisLabelFormatter'); - - // Add labels to the ticks. - for (i = 0; i < ticks.length; i++) { - if (ticks[i].label !== undefined) continue; // Use current label. - // TODO(danvk): set granularity to something appropriate here. - ticks[i].label = formatter.call(dygraph, ticks[i].v, 0, opts, dygraph); - } - - return ticks; -}; - -exports.numericTicks = numericTicks; -/** @type {Ticker} */ -var dateTicker = function dateTicker(a, b, pixels, opts, dygraph, vals) { - var chosen = pickDateTickGranularity(a, b, pixels, opts); - - if (chosen >= 0) { - return getDateAxis(a, b, chosen, opts, dygraph); - } else { - // this can happen if self.width_ is zero. - return []; - } -}; - -exports.dateTicker = dateTicker; -// Time granularity enumeration -var Granularity = { - SECONDLY: 0, - TWO_SECONDLY: 1, - FIVE_SECONDLY: 2, - TEN_SECONDLY: 3, - THIRTY_SECONDLY: 4, - MINUTELY: 5, - TWO_MINUTELY: 6, - FIVE_MINUTELY: 7, - TEN_MINUTELY: 8, - THIRTY_MINUTELY: 9, - HOURLY: 10, - TWO_HOURLY: 11, - SIX_HOURLY: 12, - DAILY: 13, - TWO_DAILY: 14, - WEEKLY: 15, - MONTHLY: 16, - QUARTERLY: 17, - BIANNUAL: 18, - ANNUAL: 19, - DECADAL: 20, - CENTENNIAL: 21, - NUM_GRANULARITIES: 22 -}; - -exports.Granularity = Granularity; -// Date components enumeration (in the order of the arguments in Date) -// TODO: make this an @enum -var DateField = { - DATEFIELD_Y: 0, - DATEFIELD_M: 1, - DATEFIELD_D: 2, - DATEFIELD_HH: 3, - DATEFIELD_MM: 4, - DATEFIELD_SS: 5, - DATEFIELD_MS: 6, - NUM_DATEFIELDS: 7 -}; - -/** - * The value of datefield will start at an even multiple of "step", i.e. - * if datefield=SS and step=5 then the first tick will be on a multiple of 5s. - * - * For granularities <= HOURLY, ticks are generated every `spacing` ms. - * - * At coarser granularities, ticks are generated by incrementing `datefield` by - * `step`. In this case, the `spacing` value is only used to estimate the - * number of ticks. It should roughly correspond to the spacing between - * adjacent ticks. - * - * @type {Array.<{datefield:number, step:number, spacing:number}>} - */ -var TICK_PLACEMENT = []; -TICK_PLACEMENT[Granularity.SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 1, spacing: 1000 * 1 }; -TICK_PLACEMENT[Granularity.TWO_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 2, spacing: 1000 * 2 }; -TICK_PLACEMENT[Granularity.FIVE_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 5, spacing: 1000 * 5 }; -TICK_PLACEMENT[Granularity.TEN_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 10, spacing: 1000 * 10 }; -TICK_PLACEMENT[Granularity.THIRTY_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 30, spacing: 1000 * 30 }; -TICK_PLACEMENT[Granularity.MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 1, spacing: 1000 * 60 }; -TICK_PLACEMENT[Granularity.TWO_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 2, spacing: 1000 * 60 * 2 }; -TICK_PLACEMENT[Granularity.FIVE_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 5, spacing: 1000 * 60 * 5 }; -TICK_PLACEMENT[Granularity.TEN_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 10, spacing: 1000 * 60 * 10 }; -TICK_PLACEMENT[Granularity.THIRTY_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 30, spacing: 1000 * 60 * 30 }; -TICK_PLACEMENT[Granularity.HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 1, spacing: 1000 * 3600 }; -TICK_PLACEMENT[Granularity.TWO_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 2, spacing: 1000 * 3600 * 2 }; -TICK_PLACEMENT[Granularity.SIX_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 6, spacing: 1000 * 3600 * 6 }; -TICK_PLACEMENT[Granularity.DAILY] = { datefield: DateField.DATEFIELD_D, step: 1, spacing: 1000 * 86400 }; -TICK_PLACEMENT[Granularity.TWO_DAILY] = { datefield: DateField.DATEFIELD_D, step: 2, spacing: 1000 * 86400 * 2 }; -TICK_PLACEMENT[Granularity.WEEKLY] = { datefield: DateField.DATEFIELD_D, step: 7, spacing: 1000 * 604800 }; -TICK_PLACEMENT[Granularity.MONTHLY] = { datefield: DateField.DATEFIELD_M, step: 1, spacing: 1000 * 7200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 12 -TICK_PLACEMENT[Granularity.QUARTERLY] = { datefield: DateField.DATEFIELD_M, step: 3, spacing: 1000 * 21600 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 4 -TICK_PLACEMENT[Granularity.BIANNUAL] = { datefield: DateField.DATEFIELD_M, step: 6, spacing: 1000 * 43200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 2 -TICK_PLACEMENT[Granularity.ANNUAL] = { datefield: DateField.DATEFIELD_Y, step: 1, spacing: 1000 * 86400 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 1 -TICK_PLACEMENT[Granularity.DECADAL] = { datefield: DateField.DATEFIELD_Y, step: 10, spacing: 1000 * 864000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 10 -TICK_PLACEMENT[Granularity.CENTENNIAL] = { datefield: DateField.DATEFIELD_Y, step: 100, spacing: 1000 * 8640000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 100 - -/** - * This is a list of human-friendly values at which to show tick marks on a log - * scale. It is k * 10^n, where k=1..9 and n=-39..+39, so: - * ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ... - * NOTE: this assumes that utils.LOG_SCALE = 10. - * @type {Array.} - */ -var PREFERRED_LOG_TICK_VALUES = (function () { - var vals = []; - for (var power = -39; power <= 39; power++) { - var range = Math.pow(10, power); - for (var mult = 1; mult <= 9; mult++) { - var val = range * mult; - vals.push(val); - } - } - return vals; -})(); - -/** - * Determine the correct granularity of ticks on a date axis. - * - * @param {number} a Left edge of the chart (ms) - * @param {number} b Right edge of the chart (ms) - * @param {number} pixels Size of the chart in the relevant dimension (width). - * @param {function(string):*} opts Function mapping from option name -> value. - * @return {number} The appropriate axis granularity for this chart. See the - * enumeration of possible values in dygraph-tickers.js. - */ -var pickDateTickGranularity = function pickDateTickGranularity(a, b, pixels, opts) { - var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel'); - for (var i = 0; i < Granularity.NUM_GRANULARITIES; i++) { - var num_ticks = numDateTicks(a, b, i); - if (pixels / num_ticks >= pixels_per_tick) { - return i; - } - } - return -1; -}; - -/** - * Compute the number of ticks on a date axis for a given granularity. - * @param {number} start_time - * @param {number} end_time - * @param {number} granularity (one of the granularities enumerated above) - * @return {number} (Approximate) number of ticks that would result. - */ -var numDateTicks = function numDateTicks(start_time, end_time, granularity) { - var spacing = TICK_PLACEMENT[granularity].spacing; - return Math.round(1.0 * (end_time - start_time) / spacing); -}; - -/** - * Compute the positions and labels of ticks on a date axis for a given granularity. - * @param {number} start_time - * @param {number} end_time - * @param {number} granularity (one of the granularities enumerated above) - * @param {function(string):*} opts Function mapping from option name -> value. - * @param {Dygraph=} dg - * @return {!TickList} - */ -var getDateAxis = function getDateAxis(start_time, end_time, granularity, opts, dg) { - var formatter = /** @type{AxisLabelFormatter} */opts("axisLabelFormatter"); - var utc = opts("labelsUTC"); - var accessors = utc ? utils.DateAccessorsUTC : utils.DateAccessorsLocal; - - var datefield = TICK_PLACEMENT[granularity].datefield; - var step = TICK_PLACEMENT[granularity].step; - var spacing = TICK_PLACEMENT[granularity].spacing; - - // Choose a nice tick position before the initial instant. - // Currently, this code deals properly with the existent daily granularities: - // DAILY (with step of 1) and WEEKLY (with step of 7 but specially handled). - // Other daily granularities (say TWO_DAILY) should also be handled specially - // by setting the start_date_offset to 0. - var start_date = new Date(start_time); - var date_array = []; - date_array[DateField.DATEFIELD_Y] = accessors.getFullYear(start_date); - date_array[DateField.DATEFIELD_M] = accessors.getMonth(start_date); - date_array[DateField.DATEFIELD_D] = accessors.getDate(start_date); - date_array[DateField.DATEFIELD_HH] = accessors.getHours(start_date); - date_array[DateField.DATEFIELD_MM] = accessors.getMinutes(start_date); - date_array[DateField.DATEFIELD_SS] = accessors.getSeconds(start_date); - date_array[DateField.DATEFIELD_MS] = accessors.getMilliseconds(start_date); - - var start_date_offset = date_array[datefield] % step; - if (granularity == Granularity.WEEKLY) { - // This will put the ticks on Sundays. - start_date_offset = accessors.getDay(start_date); - } - - date_array[datefield] -= start_date_offset; - for (var df = datefield + 1; df < DateField.NUM_DATEFIELDS; df++) { - // The minimum value is 1 for the day of month, and 0 for all other fields. - date_array[df] = df === DateField.DATEFIELD_D ? 1 : 0; - } - - // Generate the ticks. - // For granularities not coarser than HOURLY we use the fact that: - // the number of milliseconds between ticks is constant - // and equal to the defined spacing. - // Otherwise we rely on the 'roll over' property of the Date functions: - // when some date field is set to a value outside of its logical range, - // the excess 'rolls over' the next (more significant) field. - // However, when using local time with DST transitions, - // there are dates that do not represent any time value at all - // (those in the hour skipped at the 'spring forward'), - // and the JavaScript engines usually return an equivalent value. - // Hence we have to check that the date is properly increased at each step, - // returning a date at a nice tick position. - var ticks = []; - var tick_date = accessors.makeDate.apply(null, date_array); - var tick_time = tick_date.getTime(); - if (granularity <= Granularity.HOURLY) { - if (tick_time < start_time) { - tick_time += spacing; - tick_date = new Date(tick_time); - } - while (tick_time <= end_time) { - ticks.push({ v: tick_time, - label: formatter.call(dg, tick_date, granularity, opts, dg) - }); - tick_time += spacing; - tick_date = new Date(tick_time); - } - } else { - if (tick_time < start_time) { - date_array[datefield] += step; - tick_date = accessors.makeDate.apply(null, date_array); - tick_time = tick_date.getTime(); - } - while (tick_time <= end_time) { - if (granularity >= Granularity.DAILY || accessors.getHours(tick_date) % step === 0) { - ticks.push({ v: tick_time, - label: formatter.call(dg, tick_date, granularity, opts, dg) - }); - } - date_array[datefield] += step; - tick_date = accessors.makeDate.apply(null, date_array); - tick_time = tick_date.getTime(); - } - } - return ticks; -}; -exports.getDateAxis = getDateAxis; - -},{"./dygraph-utils":16}],16:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/** - * @fileoverview This file contains utility functions used by dygraphs. These - * are typically static (i.e. not related to any particular dygraph). Examples - * include date/time formatting functions, basic algorithms (e.g. binary - * search) and generic DOM-manipulation functions. - */ - -/*global Dygraph:false, Node:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.removeEvent = removeEvent; -exports.cancelEvent = cancelEvent; -exports.hsvToRGB = hsvToRGB; -exports.findPos = findPos; -exports.pageX = pageX; -exports.pageY = pageY; -exports.dragGetX_ = dragGetX_; -exports.dragGetY_ = dragGetY_; -exports.isOK = isOK; -exports.isValidPoint = isValidPoint; -exports.floatFormat = floatFormat; -exports.zeropad = zeropad; -exports.hmsString_ = hmsString_; -exports.dateString_ = dateString_; -exports.round_ = round_; -exports.binarySearch = binarySearch; -exports.dateParser = dateParser; -exports.dateStrToMillis = dateStrToMillis; -exports.update = update; -exports.updateDeep = updateDeep; -exports.isArrayLike = isArrayLike; -exports.isDateLike = isDateLike; -exports.clone = clone; -exports.createCanvas = createCanvas; -exports.getContextPixelRatio = getContextPixelRatio; -exports.isAndroid = isAndroid; -exports.Iterator = Iterator; -exports.createIterator = createIterator; -exports.repeatAndCleanup = repeatAndCleanup; -exports.isPixelChangingOptionList = isPixelChangingOptionList; -exports.detectLineDelimiter = detectLineDelimiter; -exports.isNodeContainedBy = isNodeContainedBy; -exports.pow = pow; -exports.toRGB_ = toRGB_; -exports.isCanvasSupported = isCanvasSupported; -exports.parseFloat_ = parseFloat_; -exports.numberValueFormatter = numberValueFormatter; -exports.numberAxisLabelFormatter = numberAxisLabelFormatter; -exports.dateAxisLabelFormatter = dateAxisLabelFormatter; -exports.dateValueFormatter = dateValueFormatter; - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } - -var _dygraphTickers = require('./dygraph-tickers'); - -var DygraphTickers = _interopRequireWildcard(_dygraphTickers); - -var LOG_SCALE = 10; -exports.LOG_SCALE = LOG_SCALE; -var LN_TEN = Math.log(LOG_SCALE); - -exports.LN_TEN = LN_TEN; -/** - * @private - * @param {number} x - * @return {number} - */ -var log10 = function log10(x) { - return Math.log(x) / LN_TEN; -}; - -exports.log10 = log10; -/** A dotted line stroke pattern. */ -var DOTTED_LINE = [2, 2]; -exports.DOTTED_LINE = DOTTED_LINE; -/** A dashed line stroke pattern. */ -var DASHED_LINE = [7, 3]; -exports.DASHED_LINE = DASHED_LINE; -/** A dot dash stroke pattern. */ -var DOT_DASH_LINE = [7, 2, 2, 2]; - -exports.DOT_DASH_LINE = DOT_DASH_LINE; -// Directions for panning and zooming. Use bit operations when combined -// values are possible. -var HORIZONTAL = 1; -exports.HORIZONTAL = HORIZONTAL; -var VERTICAL = 2; - -exports.VERTICAL = VERTICAL; -/** - * Return the 2d context for a dygraph canvas. - * - * This method is only exposed for the sake of replacing the function in - * automated tests. - * - * @param {!HTMLCanvasElement} canvas - * @return {!CanvasRenderingContext2D} - * @private - */ -var getContext = function getContext(canvas) { - return (/** @type{!CanvasRenderingContext2D}*/canvas.getContext("2d") - ); -}; - -exports.getContext = getContext; -/** - * Add an event handler. - * @param {!Node} elem The element to add the event to. - * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. - * @param {function(Event):(boolean|undefined)} fn The function to call - * on the event. The function takes one parameter: the event object. - * @private - */ -var addEvent = function addEvent(elem, type, fn) { - elem.addEventListener(type, fn, false); -}; - -exports.addEvent = addEvent; -/** - * Remove an event handler. - * @param {!Node} elem The element to remove the event from. - * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. - * @param {function(Event):(boolean|undefined)} fn The function to call - * on the event. The function takes one parameter: the event object. - */ - -function removeEvent(elem, type, fn) { - elem.removeEventListener(type, fn, false); -} - -; - -/** - * Cancels further processing of an event. This is useful to prevent default - * browser actions, e.g. highlighting text on a double-click. - * Based on the article at - * http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel - * @param {!Event} e The event whose normal behavior should be canceled. - * @private - */ - -function cancelEvent(e) { - e = e ? e : window.event; - if (e.stopPropagation) { - e.stopPropagation(); - } - if (e.preventDefault) { - e.preventDefault(); - } - e.cancelBubble = true; - e.cancel = true; - e.returnValue = false; - return false; -} - -; - -/** - * Convert hsv values to an rgb(r,g,b) string. Taken from MochiKit.Color. This - * is used to generate default series colors which are evenly spaced on the - * color wheel. - * @param { number } hue Range is 0.0-1.0. - * @param { number } saturation Range is 0.0-1.0. - * @param { number } value Range is 0.0-1.0. - * @return { string } "rgb(r,g,b)" where r, g and b range from 0-255. - * @private - */ - -function hsvToRGB(hue, saturation, value) { - var red; - var green; - var blue; - if (saturation === 0) { - red = value; - green = value; - blue = value; - } else { - var i = Math.floor(hue * 6); - var f = hue * 6 - i; - var p = value * (1 - saturation); - var q = value * (1 - saturation * f); - var t = value * (1 - saturation * (1 - f)); - switch (i) { - case 1: - red = q;green = value;blue = p;break; - case 2: - red = p;green = value;blue = t;break; - case 3: - red = p;green = q;blue = value;break; - case 4: - red = t;green = p;blue = value;break; - case 5: - red = value;green = p;blue = q;break; - case 6: // fall through - case 0: - red = value;green = t;blue = p;break; - } - } - red = Math.floor(255 * red + 0.5); - green = Math.floor(255 * green + 0.5); - blue = Math.floor(255 * blue + 0.5); - return 'rgb(' + red + ',' + green + ',' + blue + ')'; -} - -; - -/** - * Find the coordinates of an object relative to the top left of the page. - * - * @param {Node} obj - * @return {{x:number,y:number}} - * @private - */ - -function findPos(obj) { - var p = obj.getBoundingClientRect(), - w = window, - d = document.documentElement; - - return { - x: p.left + (w.pageXOffset || d.scrollLeft), - y: p.top + (w.pageYOffset || d.scrollTop) - }; -} - -; - -/** - * Returns the x-coordinate of the event in a coordinate system where the - * top-left corner of the page (not the window) is (0,0). - * Taken from MochiKit.Signal - * @param {!Event} e - * @return {number} - * @private - */ - -function pageX(e) { - return !e.pageX || e.pageX < 0 ? 0 : e.pageX; -} - -; - -/** - * Returns the y-coordinate of the event in a coordinate system where the - * top-left corner of the page (not the window) is (0,0). - * Taken from MochiKit.Signal - * @param {!Event} e - * @return {number} - * @private - */ - -function pageY(e) { - return !e.pageY || e.pageY < 0 ? 0 : e.pageY; -} - -; - -/** - * Converts page the x-coordinate of the event to pixel x-coordinates on the - * canvas (i.e. DOM Coords). - * @param {!Event} e Drag event. - * @param {!DygraphInteractionContext} context Interaction context object. - * @return {number} The amount by which the drag has moved to the right. - */ - -function dragGetX_(e, context) { - return pageX(e) - context.px; -} - -; - -/** - * Converts page the y-coordinate of the event to pixel y-coordinates on the - * canvas (i.e. DOM Coords). - * @param {!Event} e Drag event. - * @param {!DygraphInteractionContext} context Interaction context object. - * @return {number} The amount by which the drag has moved down. - */ - -function dragGetY_(e, context) { - return pageY(e) - context.py; -} - -; - -/** - * This returns true unless the parameter is 0, null, undefined or NaN. - * TODO(danvk): rename this function to something like 'isNonZeroNan'. - * - * @param {number} x The number to consider. - * @return {boolean} Whether the number is zero or NaN. - * @private - */ - -function isOK(x) { - return !!x && !isNaN(x); -} - -; - -/** - * @param {{x:?number,y:?number,yval:?number}} p The point to consider, valid - * points are {x, y} objects - * @param {boolean=} opt_allowNaNY Treat point with y=NaN as valid - * @return {boolean} Whether the point has numeric x and y. - * @private - */ - -function isValidPoint(p, opt_allowNaNY) { - if (!p) return false; // null or undefined object - if (p.yval === null) return false; // missing point - if (p.x === null || p.x === undefined) return false; - if (p.y === null || p.y === undefined) return false; - if (isNaN(p.x) || !opt_allowNaNY && isNaN(p.y)) return false; - return true; -} - -; - -/** - * Number formatting function which mimicks the behavior of %g in printf, i.e. - * either exponential or fixed format (without trailing 0s) is used depending on - * the length of the generated string. The advantage of this format is that - * there is a predictable upper bound on the resulting string length, - * significant figures are not dropped, and normal numbers are not displayed in - * exponential notation. - * - * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g. - * It creates strings which are too long for absolute values between 10^-4 and - * 10^-6, e.g. '0.00001' instead of '1e-5'. See tests/number-format.html for - * output examples. - * - * @param {number} x The number to format - * @param {number=} opt_precision The precision to use, default 2. - * @return {string} A string formatted like %g in printf. The max generated - * string length should be precision + 6 (e.g 1.123e+300). - */ - -function floatFormat(x, opt_precision) { - // Avoid invalid precision values; [1, 21] is the valid range. - var p = Math.min(Math.max(1, opt_precision || 2), 21); - - // This is deceptively simple. The actual algorithm comes from: - // - // Max allowed length = p + 4 - // where 4 comes from 'e+n' and '.'. - // - // Length of fixed format = 2 + y + p - // where 2 comes from '0.' and y = # of leading zeroes. - // - // Equating the two and solving for y yields y = 2, or 0.00xxxx which is - // 1.0e-3. - // - // Since the behavior of toPrecision() is identical for larger numbers, we - // don't have to worry about the other bound. - // - // Finally, the argument for toExponential() is the number of trailing digits, - // so we take off 1 for the value before the '.'. - return Math.abs(x) < 1.0e-3 && x !== 0.0 ? x.toExponential(p - 1) : x.toPrecision(p); -} - -; - -/** - * Converts '9' to '09' (useful for dates) - * @param {number} x - * @return {string} - * @private - */ - -function zeropad(x) { - if (x < 10) return "0" + x;else return "" + x; -} - -; - -/** - * Date accessors to get the parts of a calendar date (year, month, - * day, hour, minute, second and millisecond) according to local time, - * and factory method to call the Date constructor with an array of arguments. - */ -var DateAccessorsLocal = { - getFullYear: function getFullYear(d) { - return d.getFullYear(); - }, - getMonth: function getMonth(d) { - return d.getMonth(); - }, - getDate: function getDate(d) { - return d.getDate(); - }, - getHours: function getHours(d) { - return d.getHours(); - }, - getMinutes: function getMinutes(d) { - return d.getMinutes(); - }, - getSeconds: function getSeconds(d) { - return d.getSeconds(); - }, - getMilliseconds: function getMilliseconds(d) { - return d.getMilliseconds(); - }, - getDay: function getDay(d) { - return d.getDay(); - }, - makeDate: function makeDate(y, m, d, hh, mm, ss, ms) { - return new Date(y, m, d, hh, mm, ss, ms); - } -}; - -exports.DateAccessorsLocal = DateAccessorsLocal; -/** - * Date accessors to get the parts of a calendar date (year, month, - * day of month, hour, minute, second and millisecond) according to UTC time, - * and factory method to call the Date constructor with an array of arguments. - */ -var DateAccessorsUTC = { - getFullYear: function getFullYear(d) { - return d.getUTCFullYear(); - }, - getMonth: function getMonth(d) { - return d.getUTCMonth(); - }, - getDate: function getDate(d) { - return d.getUTCDate(); - }, - getHours: function getHours(d) { - return d.getUTCHours(); - }, - getMinutes: function getMinutes(d) { - return d.getUTCMinutes(); - }, - getSeconds: function getSeconds(d) { - return d.getUTCSeconds(); - }, - getMilliseconds: function getMilliseconds(d) { - return d.getUTCMilliseconds(); - }, - getDay: function getDay(d) { - return d.getUTCDay(); - }, - makeDate: function makeDate(y, m, d, hh, mm, ss, ms) { - return new Date(Date.UTC(y, m, d, hh, mm, ss, ms)); - } -}; - -exports.DateAccessorsUTC = DateAccessorsUTC; -/** - * Return a string version of the hours, minutes and seconds portion of a date. - * @param {number} hh The hours (from 0-23) - * @param {number} mm The minutes (from 0-59) - * @param {number} ss The seconds (from 0-59) - * @return {string} A time of the form "HH:MM" or "HH:MM:SS" - * @private - */ - -function hmsString_(hh, mm, ss) { - var ret = zeropad(hh) + ":" + zeropad(mm); - if (ss) { - ret += ":" + zeropad(ss); - } - return ret; -} - -; - -/** - * Convert a JS date (millis since epoch) to a formatted string. - * @param {number} time The JavaScript time value (ms since epoch) - * @param {boolean} utc Wether output UTC or local time - * @return {string} A date of one of these forms: - * "YYYY/MM/DD", "YYYY/MM/DD HH:MM" or "YYYY/MM/DD HH:MM:SS" - * @private - */ - -function dateString_(time, utc) { - var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal; - var date = new Date(time); - var y = accessors.getFullYear(date); - var m = accessors.getMonth(date); - var d = accessors.getDate(date); - var hh = accessors.getHours(date); - var mm = accessors.getMinutes(date); - var ss = accessors.getSeconds(date); - // Get a year string: - var year = "" + y; - // Get a 0 padded month string - var month = zeropad(m + 1); //months are 0-offset, sigh - // Get a 0 padded day string - var day = zeropad(d); - var frac = hh * 3600 + mm * 60 + ss; - var ret = year + "/" + month + "/" + day; - if (frac) { - ret += " " + hmsString_(hh, mm, ss); - } - return ret; -} - -; - -/** - * Round a number to the specified number of digits past the decimal point. - * @param {number} num The number to round - * @param {number} places The number of decimals to which to round - * @return {number} The rounded number - * @private - */ - -function round_(num, places) { - var shift = Math.pow(10, places); - return Math.round(num * shift) / shift; -} - -; - -/** - * Implementation of binary search over an array. - * Currently does not work when val is outside the range of arry's values. - * @param {number} val the value to search for - * @param {Array.} arry is the value over which to search - * @param {number} abs If abs > 0, find the lowest entry greater than val - * If abs < 0, find the highest entry less than val. - * If abs == 0, find the entry that equals val. - * @param {number=} low The first index in arry to consider (optional) - * @param {number=} high The last index in arry to consider (optional) - * @return {number} Index of the element, or -1 if it isn't found. - * @private - */ - -function binarySearch(_x, _x2, _x3, _x4, _x5) { - var _again = true; - - _function: while (_again) { - var val = _x, - arry = _x2, - abs = _x3, - low = _x4, - high = _x5; - _again = false; - - if (low === null || low === undefined || high === null || high === undefined) { - low = 0; - high = arry.length - 1; - } - if (low > high) { - return -1; - } - if (abs === null || abs === undefined) { - abs = 0; - } - var validIndex = function validIndex(idx) { - return idx >= 0 && idx < arry.length; - }; - var mid = parseInt((low + high) / 2, 10); - var element = arry[mid]; - var idx; - if (element == val) { - return mid; - } else if (element > val) { - if (abs > 0) { - // Accept if element > val, but also if prior element < val. - idx = mid - 1; - if (validIndex(idx) && arry[idx] < val) { - return mid; - } - } - _x = val; - _x2 = arry; - _x3 = abs; - _x4 = low; - _x5 = mid - 1; - _again = true; - validIndex = mid = element = idx = undefined; - continue _function; - } else if (element < val) { - if (abs < 0) { - // Accept if element < val, but also if prior element > val. - idx = mid + 1; - if (validIndex(idx) && arry[idx] > val) { - return mid; - } - } - _x = val; - _x2 = arry; - _x3 = abs; - _x4 = mid + 1; - _x5 = high; - _again = true; - validIndex = mid = element = idx = undefined; - continue _function; - } - return -1; // can't actually happen, but makes closure compiler happy - } -} - -; - -/** - * Parses a date, returning the number of milliseconds since epoch. This can be - * passed in as an xValueParser in the Dygraph constructor. - * TODO(danvk): enumerate formats that this understands. - * - * @param {string} dateStr A date in a variety of possible string formats. - * @return {number} Milliseconds since epoch. - * @private - */ - -function dateParser(dateStr) { - var dateStrSlashed; - var d; - - // Let the system try the format first, with one caveat: - // YYYY-MM-DD[ HH:MM:SS] is interpreted as UTC by a variety of browsers. - // dygraphs displays dates in local time, so this will result in surprising - // inconsistencies. But if you specify "T" or "Z" (i.e. YYYY-MM-DDTHH:MM:SS), - // then you probably know what you're doing, so we'll let you go ahead. - // Issue: http://code.google.com/p/dygraphs/issues/detail?id=255 - if (dateStr.search("-") == -1 || dateStr.search("T") != -1 || dateStr.search("Z") != -1) { - d = dateStrToMillis(dateStr); - if (d && !isNaN(d)) return d; - } - - if (dateStr.search("-") != -1) { - // e.g. '2009-7-12' or '2009-07-12' - dateStrSlashed = dateStr.replace("-", "/", "g"); - while (dateStrSlashed.search("-") != -1) { - dateStrSlashed = dateStrSlashed.replace("-", "/"); - } - d = dateStrToMillis(dateStrSlashed); - } else if (dateStr.length == 8) { - // e.g. '20090712' - // TODO(danvk): remove support for this format. It's confusing. - dateStrSlashed = dateStr.substr(0, 4) + "/" + dateStr.substr(4, 2) + "/" + dateStr.substr(6, 2); - d = dateStrToMillis(dateStrSlashed); - } else { - // Any format that Date.parse will accept, e.g. "2009/07/12" or - // "2009/07/12 12:34:56" - d = dateStrToMillis(dateStr); - } - - if (!d || isNaN(d)) { - console.error("Couldn't parse " + dateStr + " as a date"); - } - return d; -} - -; - -/** - * This is identical to JavaScript's built-in Date.parse() method, except that - * it doesn't get replaced with an incompatible method by aggressive JS - * libraries like MooTools or Joomla. - * @param {string} str The date string, e.g. "2011/05/06" - * @return {number} millis since epoch - * @private - */ - -function dateStrToMillis(str) { - return new Date(str).getTime(); -} - -; - -// These functions are all based on MochiKit. -/** - * Copies all the properties from o to self. - * - * @param {!Object} self - * @param {!Object} o - * @return {!Object} - */ - -function update(self, o) { - if (typeof o != 'undefined' && o !== null) { - for (var k in o) { - if (o.hasOwnProperty(k)) { - self[k] = o[k]; - } - } - } - return self; -} - -; - -/** - * Copies all the properties from o to self. - * - * @param {!Object} self - * @param {!Object} o - * @return {!Object} - * @private - */ - -function updateDeep(self, o) { - // Taken from http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object - function isNode(o) { - return typeof Node === "object" ? o instanceof Node : typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"; - } - - if (typeof o != 'undefined' && o !== null) { - for (var k in o) { - if (o.hasOwnProperty(k)) { - if (o[k] === null) { - self[k] = null; - } else if (isArrayLike(o[k])) { - self[k] = o[k].slice(); - } else if (isNode(o[k])) { - // DOM objects are shallowly-copied. - self[k] = o[k]; - } else if (typeof o[k] == 'object') { - if (typeof self[k] != 'object' || self[k] === null) { - self[k] = {}; - } - updateDeep(self[k], o[k]); - } else { - self[k] = o[k]; - } - } - } - } - return self; -} - -; - -/** - * @param {*} o - * @return {boolean} - * @private - */ - -function isArrayLike(o) { - var typ = typeof o; - if (typ != 'object' && !(typ == 'function' && typeof o.item == 'function') || o === null || typeof o.length != 'number' || o.nodeType === 3) { - return false; - } - return true; -} - -; - -/** - * @param {Object} o - * @return {boolean} - * @private - */ - -function isDateLike(o) { - if (typeof o != "object" || o === null || typeof o.getTime != 'function') { - return false; - } - return true; -} - -; - -/** - * Note: this only seems to work for arrays. - * @param {!Array} o - * @return {!Array} - * @private - */ - -function clone(o) { - // TODO(danvk): figure out how MochiKit's version works - var r = []; - for (var i = 0; i < o.length; i++) { - if (isArrayLike(o[i])) { - r.push(clone(o[i])); - } else { - r.push(o[i]); - } - } - return r; -} - -; - -/** - * Create a new canvas element. - * - * @return {!HTMLCanvasElement} - * @private - */ - -function createCanvas() { - return document.createElement('canvas'); -} - -; - -/** - * Returns the context's pixel ratio, which is the ratio between the device - * pixel ratio and the backing store ratio. Typically this is 1 for conventional - * displays, and > 1 for HiDPI displays (such as the Retina MBP). - * See http://www.html5rocks.com/en/tutorials/canvas/hidpi/ for more details. - * - * @param {!CanvasRenderingContext2D} context The canvas's 2d context. - * @return {number} The ratio of the device pixel ratio and the backing store - * ratio for the specified context. - */ - -function getContextPixelRatio(context) { - try { - var devicePixelRatio = window.devicePixelRatio; - var backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; - if (devicePixelRatio !== undefined) { - return devicePixelRatio / backingStoreRatio; - } else { - // At least devicePixelRatio must be defined for this ratio to make sense. - // We default backingStoreRatio to 1: this does not exist on some browsers - // (i.e. desktop Chrome). - return 1; - } - } catch (e) { - return 1; - } -} - -; - -/** - * Checks whether the user is on an Android browser. - * Android does not fully support the tag, e.g. w/r/t/ clipping. - * @return {boolean} - * @private - */ - -function isAndroid() { - return (/Android/.test(navigator.userAgent) - ); -} - -; - -/** - * TODO(danvk): use @template here when it's better supported for classes. - * @param {!Array} array - * @param {number} start - * @param {number} length - * @param {function(!Array,?):boolean=} predicate - * @constructor - */ - -function Iterator(array, start, length, predicate) { - start = start || 0; - length = length || array.length; - this.hasNext = true; // Use to identify if there's another element. - this.peek = null; // Use for look-ahead - this.start_ = start; - this.array_ = array; - this.predicate_ = predicate; - this.end_ = Math.min(array.length, start + length); - this.nextIdx_ = start - 1; // use -1 so initial advance works. - this.next(); // ignoring result. -} - -; - -/** - * @return {Object} - */ -Iterator.prototype.next = function () { - if (!this.hasNext) { - return null; - } - var obj = this.peek; - - var nextIdx = this.nextIdx_ + 1; - var found = false; - while (nextIdx < this.end_) { - if (!this.predicate_ || this.predicate_(this.array_, nextIdx)) { - this.peek = this.array_[nextIdx]; - found = true; - break; - } - nextIdx++; - } - this.nextIdx_ = nextIdx; - if (!found) { - this.hasNext = false; - this.peek = null; - } - return obj; -}; - -/** - * Returns a new iterator over array, between indexes start and - * start + length, and only returns entries that pass the accept function - * - * @param {!Array} array the array to iterate over. - * @param {number} start the first index to iterate over, 0 if absent. - * @param {number} length the number of elements in the array to iterate over. - * This, along with start, defines a slice of the array, and so length - * doesn't imply the number of elements in the iterator when accept doesn't - * always accept all values. array.length when absent. - * @param {function(?):boolean=} opt_predicate a function that takes - * parameters array and idx, which returns true when the element should be - * returned. If omitted, all elements are accepted. - * @private - */ - -function createIterator(array, start, length, opt_predicate) { - return new Iterator(array, start, length, opt_predicate); -} - -; - -// Shim layer with setTimeout fallback. -// From: http://paulirish.com/2011/requestanimationframe-for-smart-animating/ -// Should be called with the window context: -// Dygraph.requestAnimFrame.call(window, function() {}) -var requestAnimFrame = (function () { - return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { - window.setTimeout(callback, 1000 / 60); - }; -})(); - -exports.requestAnimFrame = requestAnimFrame; -/** - * Call a function at most maxFrames times at an attempted interval of - * framePeriodInMillis, then call a cleanup function once. repeatFn is called - * once immediately, then at most (maxFrames - 1) times asynchronously. If - * maxFrames==1, then cleanup_fn() is also called synchronously. This function - * is used to sequence animation. - * @param {function(number)} repeatFn Called repeatedly -- takes the frame - * number (from 0 to maxFrames-1) as an argument. - * @param {number} maxFrames The max number of times to call repeatFn - * @param {number} framePeriodInMillis Max requested time between frames. - * @param {function()} cleanupFn A function to call after all repeatFn calls. - * @private - */ - -function repeatAndCleanup(repeatFn, maxFrames, framePeriodInMillis, cleanupFn) { - var frameNumber = 0; - var previousFrameNumber; - var startTime = new Date().getTime(); - repeatFn(frameNumber); - if (maxFrames == 1) { - cleanupFn(); - return; - } - var maxFrameArg = maxFrames - 1; - - (function loop() { - if (frameNumber >= maxFrames) return; - requestAnimFrame.call(window, function () { - // Determine which frame to draw based on the delay so far. Will skip - // frames if necessary. - var currentTime = new Date().getTime(); - var delayInMillis = currentTime - startTime; - previousFrameNumber = frameNumber; - frameNumber = Math.floor(delayInMillis / framePeriodInMillis); - var frameDelta = frameNumber - previousFrameNumber; - // If we predict that the subsequent repeatFn call will overshoot our - // total frame target, so our last call will cause a stutter, then jump to - // the last call immediately. If we're going to cause a stutter, better - // to do it faster than slower. - var predictOvershootStutter = frameNumber + frameDelta > maxFrameArg; - if (predictOvershootStutter || frameNumber >= maxFrameArg) { - repeatFn(maxFrameArg); // Ensure final call with maxFrameArg. - cleanupFn(); - } else { - if (frameDelta !== 0) { - // Don't call repeatFn with duplicate frames. - repeatFn(frameNumber); - } - loop(); - } - }); - })(); -} - -; - -// A whitelist of options that do not change pixel positions. -var pixelSafeOptions = { - 'annotationClickHandler': true, - 'annotationDblClickHandler': true, - 'annotationMouseOutHandler': true, - 'annotationMouseOverHandler': true, - 'axisLabelColor': true, - 'axisLineColor': true, - 'axisLineWidth': true, - 'clickCallback': true, - 'drawCallback': true, - 'drawHighlightPointCallback': true, - 'drawPoints': true, - 'drawPointCallback': true, - 'drawGrid': true, - 'fillAlpha': true, - 'gridLineColor': true, - 'gridLineWidth': true, - 'hideOverlayOnMouseOut': true, - 'highlightCallback': true, - 'highlightCircleSize': true, - 'interactionModel': true, - 'isZoomedIgnoreProgrammaticZoom': true, - 'labelsDiv': true, - 'labelsDivStyles': true, - 'labelsDivWidth': true, - 'labelsKMB': true, - 'labelsKMG2': true, - 'labelsSeparateLines': true, - 'labelsShowZeroValues': true, - 'legend': true, - 'panEdgeFraction': true, - 'pixelsPerYLabel': true, - 'pointClickCallback': true, - 'pointSize': true, - 'rangeSelectorPlotFillColor': true, - 'rangeSelectorPlotFillGradientColor': true, - 'rangeSelectorPlotStrokeColor': true, - 'rangeSelectorBackgroundStrokeColor': true, - 'rangeSelectorBackgroundLineWidth': true, - 'rangeSelectorPlotLineWidth': true, - 'rangeSelectorForegroundStrokeColor': true, - 'rangeSelectorForegroundLineWidth': true, - 'rangeSelectorAlpha': true, - 'showLabelsOnHighlight': true, - 'showRoller': true, - 'strokeWidth': true, - 'underlayCallback': true, - 'unhighlightCallback': true, - 'zoomCallback': true -}; - -/** - * This function will scan the option list and determine if they - * require us to recalculate the pixel positions of each point. - * TODO: move this into dygraph-options.js - * @param {!Array.} labels a list of options to check. - * @param {!Object} attrs - * @return {boolean} true if the graph needs new points else false. - * @private - */ - -function isPixelChangingOptionList(labels, attrs) { - // Assume that we do not require new points. - // This will change to true if we actually do need new points. - - // Create a dictionary of series names for faster lookup. - // If there are no labels, then the dictionary stays empty. - var seriesNamesDictionary = {}; - if (labels) { - for (var i = 1; i < labels.length; i++) { - seriesNamesDictionary[labels[i]] = true; - } - } - - // Scan through a flat (i.e. non-nested) object of options. - // Returns true/false depending on whether new points are needed. - var scanFlatOptions = function scanFlatOptions(options) { - for (var property in options) { - if (options.hasOwnProperty(property) && !pixelSafeOptions[property]) { - return true; - } - } - return false; - }; - - // Iterate through the list of updated options. - for (var property in attrs) { - if (!attrs.hasOwnProperty(property)) continue; - - // Find out of this field is actually a series specific options list. - if (property == 'highlightSeriesOpts' || seriesNamesDictionary[property] && !attrs.series) { - // This property value is a list of options for this series. - if (scanFlatOptions(attrs[property])) return true; - } else if (property == 'series' || property == 'axes') { - // This is twice-nested options list. - var perSeries = attrs[property]; - for (var series in perSeries) { - if (perSeries.hasOwnProperty(series) && scanFlatOptions(perSeries[series])) { - return true; - } - } - } else { - // If this was not a series specific option list, check if it's a pixel - // changing property. - if (!pixelSafeOptions[property]) return true; - } - } - - return false; -} - -; - -var Circles = { - DEFAULT: function DEFAULT(g, name, ctx, canvasx, canvasy, color, radius) { - ctx.beginPath(); - ctx.fillStyle = color; - ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false); - ctx.fill(); - } - // For more shapes, include extras/shapes.js -}; - -exports.Circles = Circles; -/** - * Determine whether |data| is delimited by CR, CRLF, LF, LFCR. - * @param {string} data - * @return {?string} the delimiter that was detected (or null on failure). - */ - -function detectLineDelimiter(data) { - for (var i = 0; i < data.length; i++) { - var code = data.charAt(i); - if (code === '\r') { - // Might actually be "\r\n". - if (i + 1 < data.length && data.charAt(i + 1) === '\n') { - return '\r\n'; - } - return code; - } - if (code === '\n') { - // Might actually be "\n\r". - if (i + 1 < data.length && data.charAt(i + 1) === '\r') { - return '\n\r'; - } - return code; - } - } - - return null; -} - -; - -/** - * Is one node contained by another? - * @param {Node} containee The contained node. - * @param {Node} container The container node. - * @return {boolean} Whether containee is inside (or equal to) container. - * @private - */ - -function isNodeContainedBy(containee, container) { - if (container === null || containee === null) { - return false; - } - var containeeNode = /** @type {Node} */containee; - while (containeeNode && containeeNode !== container) { - containeeNode = containeeNode.parentNode; - } - return containeeNode === container; -} - -; - -// This masks some numeric issues in older versions of Firefox, -// where 1.0/Math.pow(10,2) != Math.pow(10,-2). -/** @type {function(number,number):number} */ - -function pow(base, exp) { - if (exp < 0) { - return 1.0 / Math.pow(base, -exp); - } - return Math.pow(base, exp); -} - -; - -var RGBA_RE = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([01](?:\.\d+)?))?\)$/; - -/** - * Helper for toRGB_ which parses strings of the form: - * rgb(123, 45, 67) - * rgba(123, 45, 67, 0.5) - * @return parsed {r,g,b,a?} tuple or null. - */ -function parseRGBA(rgbStr) { - var bits = RGBA_RE.exec(rgbStr); - if (!bits) return null; - var r = parseInt(bits[1], 10), - g = parseInt(bits[2], 10), - b = parseInt(bits[3], 10); - if (bits[4]) { - return { r: r, g: g, b: b, a: parseFloat(bits[4]) }; - } else { - return { r: r, g: g, b: b }; - } -} - -/** - * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple. - * - * @param {!string} colorStr Any valid CSS color string. - * @return {{r:number,g:number,b:number,a:number?}} Parsed RGB tuple. - * @private - */ - -function toRGB_(colorStr) { - // Strategy: First try to parse colorStr directly. This is fast & avoids DOM - // manipulation. If that fails (e.g. for named colors like 'red'), then - // create a hidden DOM element and parse its computed color. - var rgb = parseRGBA(colorStr); - if (rgb) return rgb; - - var div = document.createElement('div'); - div.style.backgroundColor = colorStr; - div.style.visibility = 'hidden'; - document.body.appendChild(div); - var rgbStr = window.getComputedStyle(div, null).backgroundColor; - document.body.removeChild(div); - return parseRGBA(rgbStr); -} - -; - -/** - * Checks whether the browser supports the <canvas> tag. - * @param {HTMLCanvasElement=} opt_canvasElement Pass a canvas element as an - * optimization if you have one. - * @return {boolean} Whether the browser supports canvas. - */ - -function isCanvasSupported(opt_canvasElement) { - try { - var canvas = opt_canvasElement || document.createElement("canvas"); - canvas.getContext("2d"); - } catch (e) { - return false; - } - return true; -} - -; - -/** - * Parses the value as a floating point number. This is like the parseFloat() - * built-in, but with a few differences: - * - the empty string is parsed as null, rather than NaN. - * - if the string cannot be parsed at all, an error is logged. - * If the string can't be parsed, this method returns null. - * @param {string} x The string to be parsed - * @param {number=} opt_line_no The line number from which the string comes. - * @param {string=} opt_line The text of the line from which the string comes. - */ - -function parseFloat_(x, opt_line_no, opt_line) { - var val = parseFloat(x); - if (!isNaN(val)) return val; - - // Try to figure out what happeend. - // If the value is the empty string, parse it as null. - if (/^ *$/.test(x)) return null; - - // If it was actually "NaN", return it as NaN. - if (/^ *nan *$/i.test(x)) return NaN; - - // Looks like a parsing error. - var msg = "Unable to parse '" + x + "' as a number"; - if (opt_line !== undefined && opt_line_no !== undefined) { - msg += " on line " + (1 + (opt_line_no || 0)) + " ('" + opt_line + "') of CSV."; - } - console.error(msg); - - return null; -} - -; - -// Label constants for the labelsKMB and labelsKMG2 options. -// (i.e. '100000' -> '100K') -var KMB_LABELS = ['K', 'M', 'B', 'T', 'Q']; -var KMG2_BIG_LABELS = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; -var KMG2_SMALL_LABELS = ['m', 'u', 'n', 'p', 'f', 'a', 'z', 'y']; - -/** - * @private - * Return a string version of a number. This respects the digitsAfterDecimal - * and maxNumberWidth options. - * @param {number} x The number to be formatted - * @param {Dygraph} opts An options view - */ - -function numberValueFormatter(x, opts) { - var sigFigs = opts('sigFigs'); - - if (sigFigs !== null) { - // User has opted for a fixed number of significant figures. - return floatFormat(x, sigFigs); - } - - var digits = opts('digitsAfterDecimal'); - var maxNumberWidth = opts('maxNumberWidth'); - - var kmb = opts('labelsKMB'); - var kmg2 = opts('labelsKMG2'); - - var label; - - // switch to scientific notation if we underflow or overflow fixed display. - if (x !== 0.0 && (Math.abs(x) >= Math.pow(10, maxNumberWidth) || Math.abs(x) < Math.pow(10, -digits))) { - label = x.toExponential(digits); - } else { - label = '' + round_(x, digits); - } - - if (kmb || kmg2) { - var k; - var k_labels = []; - var m_labels = []; - if (kmb) { - k = 1000; - k_labels = KMB_LABELS; - } - if (kmg2) { - if (kmb) console.warn("Setting both labelsKMB and labelsKMG2. Pick one!"); - k = 1024; - k_labels = KMG2_BIG_LABELS; - m_labels = KMG2_SMALL_LABELS; - } - - var absx = Math.abs(x); - var n = pow(k, k_labels.length); - for (var j = k_labels.length - 1; j >= 0; j--, n /= k) { - if (absx >= n) { - label = round_(x / n, digits) + k_labels[j]; - break; - } - } - if (kmg2) { - // TODO(danvk): clean up this logic. Why so different than kmb? - var x_parts = String(x.toExponential()).split('e-'); - if (x_parts.length === 2 && x_parts[1] >= 3 && x_parts[1] <= 24) { - if (x_parts[1] % 3 > 0) { - label = round_(x_parts[0] / pow(10, x_parts[1] % 3), digits); - } else { - label = Number(x_parts[0]).toFixed(2); - } - label += m_labels[Math.floor(x_parts[1] / 3) - 1]; - } - } - } - - return label; -} - -; - -/** - * variant for use as an axisLabelFormatter. - * @private - */ - -function numberAxisLabelFormatter(x, granularity, opts) { - return numberValueFormatter.call(this, x, opts); -} - -; - -/** - * @type {!Array.} - * @private - * @constant - */ -var SHORT_MONTH_NAMES_ = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - -/** - * Convert a JS date to a string appropriate to display on an axis that - * is displaying values at the stated granularity. This respects the - * labelsUTC option. - * @param {Date} date The date to format - * @param {number} granularity One of the Dygraph granularity constants - * @param {Dygraph} opts An options view - * @return {string} The date formatted as local time - * @private - */ - -function dateAxisLabelFormatter(date, granularity, opts) { - var utc = opts('labelsUTC'); - var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal; - - var year = accessors.getFullYear(date), - month = accessors.getMonth(date), - day = accessors.getDate(date), - hours = accessors.getHours(date), - mins = accessors.getMinutes(date), - secs = accessors.getSeconds(date), - millis = accessors.getSeconds(date); - - if (granularity >= DygraphTickers.Granularity.DECADAL) { - return '' + year; - } else if (granularity >= DygraphTickers.Granularity.MONTHLY) { - return SHORT_MONTH_NAMES_[month] + ' ' + year; - } else { - var frac = hours * 3600 + mins * 60 + secs + 1e-3 * millis; - if (frac === 0 || granularity >= DygraphTickers.Granularity.DAILY) { - // e.g. '21 Jan' (%d%b) - return zeropad(day) + ' ' + SHORT_MONTH_NAMES_[month]; - } else { - return hmsString_(hours, mins, secs); - } - } -} - -; -// alias in case anyone is referencing the old method. -// Dygraph.dateAxisFormatter = Dygraph.dateAxisLabelFormatter; - -/** - * Return a string version of a JS date for a value label. This respects the - * labelsUTC option. - * @param {Date} date The date to be formatted - * @param {Dygraph} opts An options view - * @private - */ - -function dateValueFormatter(d, opts) { - return dateString_(d, opts('labelsUTC')); -} - -; - -},{"./dygraph-tickers":15}],17:[function(require,module,exports){ -/** - * @license - * Copyright 2006 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ /** - * @fileoverview Creates an interactive, zoomable graph based on a CSV file or - * string. Dygraph can handle multiple series with or without error bars. The - * date/value ranges will be automatically set. Dygraph uses the - * <canvas> tag, so it only works in FF1.5+. - * @author danvdk@gmail.com (Dan Vanderkam) - - Usage: -
- - - The CSV file is of the form - - Date,SeriesA,SeriesB,SeriesC - YYYYMMDD,A1,B1,C1 - YYYYMMDD,A2,B2,C2 - - If the 'errorBars' option is set in the constructor, the input should be of - the form - Date,SeriesA,SeriesB,... - YYYYMMDD,A1,sigmaA1,B1,sigmaB1,... - YYYYMMDD,A2,sigmaA2,B2,sigmaB2,... - - If the 'fractions' option is set, the input should be of the form: - - Date,SeriesA,SeriesB,... - YYYYMMDD,A1/B1,A2/B2,... - YYYYMMDD,A1/B1,A2/B2,... - - And error bars will be calculated automatically using a binomial distribution. - - For further documentation and examples, see http://dygraphs.com/ - - */'use strict';Object.defineProperty(exports,'__esModule',{value:true});function _interopRequireWildcard(obj){if(obj && obj.__esModule){return obj;}else {var newObj={};if(obj != null){for(var key in obj) {if(Object.prototype.hasOwnProperty.call(obj,key))newObj[key] = obj[key];}}newObj['default'] = obj;return newObj;}}function _interopRequireDefault(obj){return obj && obj.__esModule?obj:{'default':obj};}var _dygraphLayout=require('./dygraph-layout');var _dygraphLayout2=_interopRequireDefault(_dygraphLayout);var _dygraphCanvas=require('./dygraph-canvas');var _dygraphCanvas2=_interopRequireDefault(_dygraphCanvas);var _dygraphOptions=require('./dygraph-options');var _dygraphOptions2=_interopRequireDefault(_dygraphOptions);var _dygraphInteractionModel=require('./dygraph-interaction-model');var _dygraphInteractionModel2=_interopRequireDefault(_dygraphInteractionModel);var _dygraphTickers=require('./dygraph-tickers');var DygraphTickers=_interopRequireWildcard(_dygraphTickers);var _dygraphUtils=require('./dygraph-utils');var utils=_interopRequireWildcard(_dygraphUtils);var _dygraphDefaultAttrs=require('./dygraph-default-attrs');var _dygraphDefaultAttrs2=_interopRequireDefault(_dygraphDefaultAttrs);var _dygraphOptionsReference=require('./dygraph-options-reference');var _dygraphOptionsReference2=_interopRequireDefault(_dygraphOptionsReference);var _iframeTarp=require('./iframe-tarp');var _iframeTarp2=_interopRequireDefault(_iframeTarp);var _datahandlerDefault=require('./datahandler/default');var _datahandlerDefault2=_interopRequireDefault(_datahandlerDefault);var _datahandlerBarsError=require('./datahandler/bars-error');var _datahandlerBarsError2=_interopRequireDefault(_datahandlerBarsError);var _datahandlerBarsCustom=require('./datahandler/bars-custom');var _datahandlerBarsCustom2=_interopRequireDefault(_datahandlerBarsCustom);var _datahandlerDefaultFractions=require('./datahandler/default-fractions');var _datahandlerDefaultFractions2=_interopRequireDefault(_datahandlerDefaultFractions);var _datahandlerBarsFractions=require('./datahandler/bars-fractions');var _datahandlerBarsFractions2=_interopRequireDefault(_datahandlerBarsFractions);var _datahandlerBars=require('./datahandler/bars');var _datahandlerBars2=_interopRequireDefault(_datahandlerBars);var _pluginsAnnotations=require('./plugins/annotations');var _pluginsAnnotations2=_interopRequireDefault(_pluginsAnnotations);var _pluginsAxes=require('./plugins/axes');var _pluginsAxes2=_interopRequireDefault(_pluginsAxes);var _pluginsChartLabels=require('./plugins/chart-labels');var _pluginsChartLabels2=_interopRequireDefault(_pluginsChartLabels);var _pluginsGrid=require('./plugins/grid');var _pluginsGrid2=_interopRequireDefault(_pluginsGrid);var _pluginsLegend=require('./plugins/legend');var _pluginsLegend2=_interopRequireDefault(_pluginsLegend);var _pluginsRangeSelector=require('./plugins/range-selector');var _pluginsRangeSelector2=_interopRequireDefault(_pluginsRangeSelector);var _dygraphGviz=require('./dygraph-gviz');var _dygraphGviz2=_interopRequireDefault(_dygraphGviz);"use strict"; /** - * Creates an interactive, zoomable chart. - * - * @constructor - * @param {div | String} div A div or the id of a div into which to construct - * the chart. - * @param {String | Function} file A file containing CSV data or a function - * that returns this data. The most basic expected format for each line is - * "YYYY/MM/DD,val1,val2,...". For more information, see - * http://dygraphs.com/data.html. - * @param {Object} attrs Various other attributes, e.g. errorBars determines - * whether the input data contains error ranges. For a complete list of - * options, see http://dygraphs.com/options.html. - */var Dygraph=function Dygraph(div,data,opts){this.__init__(div,data,opts);};Dygraph.NAME = "Dygraph";Dygraph.VERSION = "1.1.0"; // Various default values -Dygraph.DEFAULT_ROLL_PERIOD = 1;Dygraph.DEFAULT_WIDTH = 480;Dygraph.DEFAULT_HEIGHT = 320; // For max 60 Hz. animation: -Dygraph.ANIMATION_STEPS = 12;Dygraph.ANIMATION_DURATION = 200; /** - * Standard plotters. These may be used by clients. - * Available plotters are: - * - Dygraph.Plotters.linePlotter: draws central lines (most common) - * - Dygraph.Plotters.errorPlotter: draws error bars - * - Dygraph.Plotters.fillPlotter: draws fills under lines (used with fillGraph) - * - * By default, the plotter is [fillPlotter, errorPlotter, linePlotter]. - * This causes all the lines to be drawn over all the fills/error bars. - */Dygraph.Plotters = _dygraphCanvas2['default']._Plotters; // Used for initializing annotation CSS rules only once. -Dygraph.addedAnnotationCSS = false; /** - * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit - * and context <canvas> inside of it. See the constructor for details. - * on the parameters. - * @param {Element} div the Element to render the graph into. - * @param {string | Function} file Source data - * @param {Object} attrs Miscellaneous other options - * @private - */Dygraph.prototype.__init__ = function(div,file,attrs){this.is_initial_draw_ = true;this.readyFns_ = []; // Support two-argument constructor -if(attrs === null || attrs === undefined){attrs = {};}attrs = Dygraph.copyUserAttrs_(attrs);if(typeof div == 'string'){div = document.getElementById(div);}if(!div){throw new Error('Constructing dygraph with a non-existent div!');} // Copy the important bits into the object -// TODO(danvk): most of these should just stay in the attrs_ dictionary. -this.maindiv_ = div;this.file_ = file;this.rollPeriod_ = attrs.rollPeriod || Dygraph.DEFAULT_ROLL_PERIOD;this.previousVerticalX_ = -1;this.fractions_ = attrs.fractions || false;this.dateWindow_ = attrs.dateWindow || null;this.annotations_ = []; // Zoomed indicators - These indicate when the graph has been zoomed and on what axis. -this.zoomed_x_ = false;this.zoomed_y_ = false; // Clear the div. This ensure that, if multiple dygraphs are passed the same -// div, then only one will be drawn. -div.innerHTML = ""; // For historical reasons, the 'width' and 'height' options trump all CSS -// rules _except_ for an explicit 'width' or 'height' on the div. -// As an added convenience, if the div has zero height (like
does -// without any styles), then we use a default height/width. -if(div.style.width === '' && attrs.width){div.style.width = attrs.width + "px";}if(div.style.height === '' && attrs.height){div.style.height = attrs.height + "px";}if(div.style.height === '' && div.clientHeight === 0){div.style.height = Dygraph.DEFAULT_HEIGHT + "px";if(div.style.width === ''){div.style.width = Dygraph.DEFAULT_WIDTH + "px";}} // These will be zero if the dygraph's div is hidden. In that case, -// use the user-specified attributes if present. If not, use zero -// and assume the user will call resize to fix things later. -this.width_ = div.clientWidth || attrs.width || 0;this.height_ = div.clientHeight || attrs.height || 0; // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_. -if(attrs.stackedGraph){attrs.fillGraph = true; // TODO(nikhilk): Add any other stackedGraph checks here. -} // DEPRECATION WARNING: All option processing should be moved from -// attrs_ and user_attrs_ to options_, which holds all this information. -// -// Dygraphs has many options, some of which interact with one another. -// To keep track of everything, we maintain two sets of options: -// -// this.user_attrs_ only options explicitly set by the user. -// this.attrs_ defaults, options derived from user_attrs_, data. -// -// Options are then accessed this.attr_('attr'), which first looks at -// user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent -// defaults without overriding behavior that the user specifically asks for. -this.user_attrs_ = {};utils.update(this.user_attrs_,attrs); // This sequence ensures that Dygraph.DEFAULT_ATTRS is never modified. -this.attrs_ = {};utils.updateDeep(this.attrs_,_dygraphDefaultAttrs2['default']);this.boundaryIds_ = [];this.setIndexByName_ = {};this.datasetIndex_ = [];this.registeredEvents_ = [];this.eventListeners_ = {};this.attributes_ = new _dygraphOptions2['default'](this); // Create the containing DIV and other interactive elements -this.createInterface_(); // Activate plugins. -this.plugins_ = [];var plugins=Dygraph.PLUGINS.concat(this.getOption('plugins'));for(var i=0;i < plugins.length;i++) { // the plugins option may contain either plugin classes or instances. -// Plugin instances contain an activate method. -var Plugin=plugins[i]; // either a constructor or an instance. -var pluginInstance;if(typeof Plugin.activate !== 'undefined'){pluginInstance = Plugin;}else {pluginInstance = new Plugin();}var pluginDict={plugin:pluginInstance,events:{},options:{},pluginOptions:{}};var handlers=pluginInstance.activate(this);for(var eventName in handlers) {if(!handlers.hasOwnProperty(eventName))continue; // TODO(danvk): validate eventName. -pluginDict.events[eventName] = handlers[eventName];}this.plugins_.push(pluginDict);} // At this point, plugins can no longer register event handlers. -// Construct a map from event -> ordered list of [callback, plugin]. -for(var i=0;i < this.plugins_.length;i++) {var plugin_dict=this.plugins_[i];for(var eventName in plugin_dict.events) {if(!plugin_dict.events.hasOwnProperty(eventName))continue;var callback=plugin_dict.events[eventName];var pair=[plugin_dict.plugin,callback];if(!(eventName in this.eventListeners_)){this.eventListeners_[eventName] = [pair];}else {this.eventListeners_[eventName].push(pair);}}}this.createDragInterface_();this.start_();}; /** - * Triggers a cascade of events to the various plugins which are interested in them. - * Returns true if the "default behavior" should be prevented, i.e. if one - * of the event listeners called event.preventDefault(). - * @private - */Dygraph.prototype.cascadeEvents_ = function(name,extra_props){if(!(name in this.eventListeners_))return false; // QUESTION: can we use objects & prototypes to speed this up? -var e={dygraph:this,cancelable:false,defaultPrevented:false,preventDefault:function preventDefault(){if(!e.cancelable)throw "Cannot call preventDefault on non-cancelable event.";e.defaultPrevented = true;},propagationStopped:false,stopPropagation:function stopPropagation(){e.propagationStopped = true;}};utils.update(e,extra_props);var callback_plugin_pairs=this.eventListeners_[name];if(callback_plugin_pairs){for(var i=callback_plugin_pairs.length - 1;i >= 0;i--) {var plugin=callback_plugin_pairs[i][0];var callback=callback_plugin_pairs[i][1];callback.call(plugin,e);if(e.propagationStopped)break;}}return e.defaultPrevented;}; /** - * Fetch a plugin instance of a particular class. Only for testing. - * @private - * @param {!Class} type The type of the plugin. - * @return {Object} Instance of the plugin, or null if there is none. - */Dygraph.prototype.getPluginInstance_ = function(type){for(var i=0;i < this.plugins_.length;i++) {var p=this.plugins_[i];if(p.plugin instanceof type){return p.plugin;}}return null;}; /** - * Returns the zoomed status of the chart for one or both axes. - * - * Axis is an optional parameter. Can be set to 'x' or 'y'. - * - * The zoomed status for an axis is set whenever a user zooms using the mouse - * or when the dateWindow or valueRange are updated (unless the - * isZoomedIgnoreProgrammaticZoom option is also specified). - */Dygraph.prototype.isZoomed = function(axis){if(axis === null || axis === undefined){return this.zoomed_x_ || this.zoomed_y_;}if(axis === 'x')return this.zoomed_x_;if(axis === 'y')return this.zoomed_y_;throw "axis parameter is [" + axis + "] must be null, 'x' or 'y'.";}; /** - * Returns information about the Dygraph object, including its containing ID. - */Dygraph.prototype.toString = function(){var maindiv=this.maindiv_;var id=maindiv && maindiv.id?maindiv.id:maindiv;return "[Dygraph " + id + "]";}; /** - * @private - * Returns the value of an option. This may be set by the user (either in the - * constructor or by calling updateOptions) or by dygraphs, and may be set to a - * per-series value. - * @param {string} name The name of the option, e.g. 'rollPeriod'. - * @param {string} [seriesName] The name of the series to which the option - * will be applied. If no per-series value of this option is available, then - * the global value is returned. This is optional. - * @return { ... } The value of the option. - */Dygraph.prototype.attr_ = function(name,seriesName){ // For "production" code, this gets removed by uglifyjs. -if("development" != 'production'){if(typeof _dygraphOptionsReference2['default'] === 'undefined'){console.error('Must include options reference JS for testing');}else if(!_dygraphOptionsReference2['default'].hasOwnProperty(name)){console.error('Dygraphs is using property ' + name + ', which has no ' + 'entry in the Dygraphs.OPTIONS_REFERENCE listing.'); // Only log this error once. -_dygraphOptionsReference2['default'][name] = true;}}return seriesName?this.attributes_.getForSeries(name,seriesName):this.attributes_.get(name);}; /** - * Returns the current value for an option, as set in the constructor or via - * updateOptions. You may pass in an (optional) series name to get per-series - * values for the option. - * - * All values returned by this method should be considered immutable. If you - * modify them, there is no guarantee that the changes will be honored or that - * dygraphs will remain in a consistent state. If you want to modify an option, - * use updateOptions() instead. - * - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {*} The value of the option. - */Dygraph.prototype.getOption = function(name,opt_seriesName){return this.attr_(name,opt_seriesName);}; /** - * Like getOption(), but specifically returns a number. - * This is a convenience function for working with the Closure Compiler. - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {number} The value of the option. - * @private - */Dygraph.prototype.getNumericOption = function(name,opt_seriesName){return (/** @type{number} */this.getOption(name,opt_seriesName));}; /** - * Like getOption(), but specifically returns a string. - * This is a convenience function for working with the Closure Compiler. - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {string} The value of the option. - * @private - */Dygraph.prototype.getStringOption = function(name,opt_seriesName){return (/** @type{string} */this.getOption(name,opt_seriesName));}; /** - * Like getOption(), but specifically returns a boolean. - * This is a convenience function for working with the Closure Compiler. - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {boolean} The value of the option. - * @private - */Dygraph.prototype.getBooleanOption = function(name,opt_seriesName){return (/** @type{boolean} */this.getOption(name,opt_seriesName));}; /** - * Like getOption(), but specifically returns a function. - * This is a convenience function for working with the Closure Compiler. - * @param {string} name The name of the option (e.g. 'strokeWidth') - * @param {string=} opt_seriesName Series name to get per-series values. - * @return {function(...)} The value of the option. - * @private - */Dygraph.prototype.getFunctionOption = function(name,opt_seriesName){return (/** @type{function(...)} */this.getOption(name,opt_seriesName));};Dygraph.prototype.getOptionForAxis = function(name,axis){return this.attributes_.getForAxis(name,axis);}; /** - * @private - * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2') - * @return { ... } A function mapping string -> option value - */Dygraph.prototype.optionsViewForAxis_ = function(axis){var self=this;return function(opt){var axis_opts=self.user_attrs_.axes;if(axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)){return axis_opts[axis][opt];} // I don't like that this is in a second spot. -if(axis === 'x' && opt === 'logscale'){ // return the default value. -// TODO(konigsberg): pull the default from a global default. -return false;} // user-specified attributes always trump defaults, even if they're less -// specific. -if(typeof self.user_attrs_[opt] != 'undefined'){return self.user_attrs_[opt];}axis_opts = self.attrs_.axes;if(axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)){return axis_opts[axis][opt];} // check old-style axis options -// TODO(danvk): add a deprecation warning if either of these match. -if(axis == 'y' && self.axes_[0].hasOwnProperty(opt)){return self.axes_[0][opt];}else if(axis == 'y2' && self.axes_[1].hasOwnProperty(opt)){return self.axes_[1][opt];}return self.attr_(opt);};}; /** - * Returns the current rolling period, as set by the user or an option. - * @return {number} The number of points in the rolling window - */Dygraph.prototype.rollPeriod = function(){return this.rollPeriod_;}; /** - * Returns the currently-visible x-range. This can be affected by zooming, - * panning or a call to updateOptions. - * Returns a two-element array: [left, right]. - * If the Dygraph has dates on the x-axis, these will be millis since epoch. - */Dygraph.prototype.xAxisRange = function(){return this.dateWindow_?this.dateWindow_:this.xAxisExtremes();}; /** - * Returns the lower- and upper-bound x-axis values of the - * data set. - */Dygraph.prototype.xAxisExtremes = function(){var pad=this.getNumericOption('xRangePad') / this.plotter_.area.w;if(this.numRows() === 0){return [0 - pad,1 + pad];}var left=this.rawData_[0][0];var right=this.rawData_[this.rawData_.length - 1][0];if(pad){ // Must keep this in sync with dygraph-layout _evaluateLimits() -var range=right - left;left -= range * pad;right += range * pad;}return [left,right];}; /** - * Returns the currently-visible y-range for an axis. This can be affected by - * zooming, panning or a call to updateOptions. Axis indices are zero-based. If - * called with no arguments, returns the range of the first axis. - * Returns a two-element array: [bottom, top]. - */Dygraph.prototype.yAxisRange = function(idx){if(typeof idx == "undefined")idx = 0;if(idx < 0 || idx >= this.axes_.length){return null;}var axis=this.axes_[idx];return [axis.computedValueRange[0],axis.computedValueRange[1]];}; /** - * Returns the currently-visible y-ranges for each axis. This can be affected by - * zooming, panning, calls to updateOptions, etc. - * Returns an array of [bottom, top] pairs, one for each y-axis. - */Dygraph.prototype.yAxisRanges = function(){var ret=[];for(var i=0;i < this.axes_.length;i++) {ret.push(this.yAxisRange(i));}return ret;}; // TODO(danvk): use these functions throughout dygraphs. -/** - * Convert from data coordinates to canvas/div X/Y coordinates. - * If specified, do this conversion for the coordinate system of a particular - * axis. Uses the first axis by default. - * Returns a two-element array: [X, Y] - * - * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord - * instead of toDomCoords(null, y, axis). - */Dygraph.prototype.toDomCoords = function(x,y,axis){return [this.toDomXCoord(x),this.toDomYCoord(y,axis)];}; /** - * Convert from data x coordinates to canvas/div X coordinate. - * If specified, do this conversion for the coordinate system of a particular - * axis. - * Returns a single value or null if x is null. - */Dygraph.prototype.toDomXCoord = function(x){if(x === null){return null;}var area=this.plotter_.area;var xRange=this.xAxisRange();return area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w;}; /** - * Convert from data x coordinates to canvas/div Y coordinate and optional - * axis. Uses the first axis by default. - * - * returns a single value or null if y is null. - */Dygraph.prototype.toDomYCoord = function(y,axis){var pct=this.toPercentYCoord(y,axis);if(pct === null){return null;}var area=this.plotter_.area;return area.y + pct * area.h;}; /** - * Convert from canvas/div coords to data coordinates. - * If specified, do this conversion for the coordinate system of a particular - * axis. Uses the first axis by default. - * Returns a two-element array: [X, Y]. - * - * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord - * instead of toDataCoords(null, y, axis). - */Dygraph.prototype.toDataCoords = function(x,y,axis){return [this.toDataXCoord(x),this.toDataYCoord(y,axis)];}; /** - * Convert from canvas/div x coordinate to data coordinate. - * - * If x is null, this returns null. - */Dygraph.prototype.toDataXCoord = function(x){if(x === null){return null;}var area=this.plotter_.area;var xRange=this.xAxisRange();if(!this.attributes_.getForAxis("logscale",'x')){return xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]);}else { // TODO: remove duplicate code? -// Computing the inverse of toDomCoord. -var pct=(x - area.x) / area.w; // Computing the inverse of toPercentXCoord. The function was arrived at with -// the following steps: -// -// Original calcuation: -// pct = (log(x) - log(xRange[0])) / (log(xRange[1]) - log(xRange[0]))); -// -// Multiply both sides by the right-side demoninator. -// pct * (log(xRange[1] - log(xRange[0]))) = log(x) - log(xRange[0]) -// -// add log(xRange[0]) to both sides -// log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])) = log(x); -// -// Swap both sides of the equation, -// log(x) = log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])) -// -// Use both sides as the exponent in 10^exp and we're done. -// x = 10 ^ (log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0]))) -var logr0=utils.log10(xRange[0]);var logr1=utils.log10(xRange[1]);var exponent=logr0 + pct * (logr1 - logr0);var value=Math.pow(utils.LOG_SCALE,exponent);return value;}}; /** - * Convert from canvas/div y coord to value. - * - * If y is null, this returns null. - * if axis is null, this uses the first axis. - */Dygraph.prototype.toDataYCoord = function(y,axis){if(y === null){return null;}var area=this.plotter_.area;var yRange=this.yAxisRange(axis);if(typeof axis == "undefined")axis = 0;if(!this.attributes_.getForAxis("logscale",axis)){return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]);}else { // Computing the inverse of toDomCoord. -var pct=(y - area.y) / area.h; // Computing the inverse of toPercentYCoord. The function was arrived at with -// the following steps: -// -// Original calcuation: -// pct = (log(yRange[1]) - log(y)) / (log(yRange[1]) - log(yRange[0])); -// -// Multiply both sides by the right-side demoninator. -// pct * (log(yRange[1]) - log(yRange[0])) = log(yRange[1]) - log(y); -// -// subtract log(yRange[1]) from both sides. -// (pct * (log(yRange[1]) - log(yRange[0]))) - log(yRange[1]) = -log(y); -// -// and multiply both sides by -1. -// log(yRange[1]) - (pct * (logr1 - log(yRange[0])) = log(y); -// -// Swap both sides of the equation, -// log(y) = log(yRange[1]) - (pct * (log(yRange[1]) - log(yRange[0]))); -// -// Use both sides as the exponent in 10^exp and we're done. -// y = 10 ^ (log(yRange[1]) - (pct * (log(yRange[1]) - log(yRange[0])))); -var logr0=utils.log10(yRange[0]);var logr1=utils.log10(yRange[1]);var exponent=logr1 - pct * (logr1 - logr0);var value=Math.pow(utils.LOG_SCALE,exponent);return value;}}; /** - * Converts a y for an axis to a percentage from the top to the - * bottom of the drawing area. - * - * If the coordinate represents a value visible on the canvas, then - * the value will be between 0 and 1, where 0 is the top of the canvas. - * However, this method will return values outside the range, as - * values can fall outside the canvas. - * - * If y is null, this returns null. - * if axis is null, this uses the first axis. - * - * @param {number} y The data y-coordinate. - * @param {number} [axis] The axis number on which the data coordinate lives. - * @return {number} A fraction in [0, 1] where 0 = the top edge. - */Dygraph.prototype.toPercentYCoord = function(y,axis){if(y === null){return null;}if(typeof axis == "undefined")axis = 0;var yRange=this.yAxisRange(axis);var pct;var logscale=this.attributes_.getForAxis("logscale",axis);if(logscale){var logr0=utils.log10(yRange[0]);var logr1=utils.log10(yRange[1]);pct = (logr1 - utils.log10(y)) / (logr1 - logr0);}else { // yRange[1] - y is unit distance from the bottom. -// yRange[1] - yRange[0] is the scale of the range. -// (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom. -pct = (yRange[1] - y) / (yRange[1] - yRange[0]);}return pct;}; /** - * Converts an x value to a percentage from the left to the right of - * the drawing area. - * - * If the coordinate represents a value visible on the canvas, then - * the value will be between 0 and 1, where 0 is the left of the canvas. - * However, this method will return values outside the range, as - * values can fall outside the canvas. - * - * If x is null, this returns null. - * @param {number} x The data x-coordinate. - * @return {number} A fraction in [0, 1] where 0 = the left edge. - */Dygraph.prototype.toPercentXCoord = function(x){if(x === null){return null;}var xRange=this.xAxisRange();var pct;var logscale=this.attributes_.getForAxis("logscale",'x');if(logscale === true){ // logscale can be null so we test for true explicitly. -var logr0=utils.log10(xRange[0]);var logr1=utils.log10(xRange[1]);pct = (utils.log10(x) - logr0) / (logr1 - logr0);}else { // x - xRange[0] is unit distance from the left. -// xRange[1] - xRange[0] is the scale of the range. -// The full expression below is the % from the left. -pct = (x - xRange[0]) / (xRange[1] - xRange[0]);}return pct;}; /** - * Returns the number of columns (including the independent variable). - * @return {number} The number of columns. - */Dygraph.prototype.numColumns = function(){if(!this.rawData_)return 0;return this.rawData_[0]?this.rawData_[0].length:this.attr_("labels").length;}; /** - * Returns the number of rows (excluding any header/label row). - * @return {number} The number of rows, less any header. - */Dygraph.prototype.numRows = function(){if(!this.rawData_)return 0;return this.rawData_.length;}; /** - * Returns the value in the given row and column. If the row and column exceed - * the bounds on the data, returns null. Also returns null if the value is - * missing. - * @param {number} row The row number of the data (0-based). Row 0 is the - * first row of data, not a header row. - * @param {number} col The column number of the data (0-based) - * @return {number} The value in the specified cell or null if the row/col - * were out of range. - */Dygraph.prototype.getValue = function(row,col){if(row < 0 || row > this.rawData_.length)return null;if(col < 0 || col > this.rawData_[row].length)return null;return this.rawData_[row][col];}; /** - * Generates interface elements for the Dygraph: a containing div, a div to - * display the current point, and a textbox to adjust the rolling average - * period. Also creates the Renderer/Layout elements. - * @private - */Dygraph.prototype.createInterface_ = function(){ // Create the all-enclosing graph div -var enclosing=this.maindiv_;this.graphDiv = document.createElement("div"); // TODO(danvk): any other styles that are useful to set here? -this.graphDiv.style.textAlign = 'left'; // This is a CSS "reset" -this.graphDiv.style.position = 'relative';enclosing.appendChild(this.graphDiv); // Create the canvas for interactive parts of the chart. -this.canvas_ = utils.createCanvas();this.canvas_.style.position = "absolute"; // ... and for static parts of the chart. -this.hidden_ = this.createPlotKitCanvas_(this.canvas_);this.canvas_ctx_ = utils.getContext(this.canvas_);this.hidden_ctx_ = utils.getContext(this.hidden_);this.resizeElements_(); // The interactive parts of the graph are drawn on top of the chart. -this.graphDiv.appendChild(this.hidden_);this.graphDiv.appendChild(this.canvas_);this.mouseEventElement_ = this.createMouseEventElement_(); // Create the grapher -this.layout_ = new _dygraphLayout2['default'](this);var dygraph=this;this.mouseMoveHandler_ = function(e){dygraph.mouseMove_(e);};this.mouseOutHandler_ = function(e){ // The mouse has left the chart if: -// 1. e.target is inside the chart -// 2. e.relatedTarget is outside the chart -var target=e.target || e.fromElement;var relatedTarget=e.relatedTarget || e.toElement;if(utils.isNodeContainedBy(target,dygraph.graphDiv) && !utils.isNodeContainedBy(relatedTarget,dygraph.graphDiv)){dygraph.mouseOut_(e);}};this.addAndTrackEvent(window,'mouseout',this.mouseOutHandler_);this.addAndTrackEvent(this.mouseEventElement_,'mousemove',this.mouseMoveHandler_); // Don't recreate and register the resize handler on subsequent calls. -// This happens when the graph is resized. -if(!this.resizeHandler_){this.resizeHandler_ = function(e){dygraph.resize();}; // Update when the window is resized. -// TODO(danvk): drop frames depending on complexity of the chart. -this.addAndTrackEvent(window,'resize',this.resizeHandler_);}};Dygraph.prototype.resizeElements_ = function(){this.graphDiv.style.width = this.width_ + "px";this.graphDiv.style.height = this.height_ + "px";var canvasScale=utils.getContextPixelRatio(this.canvas_ctx_);this.canvas_.width = this.width_ * canvasScale;this.canvas_.height = this.height_ * canvasScale;this.canvas_.style.width = this.width_ + "px"; // for IE -this.canvas_.style.height = this.height_ + "px"; // for IE -if(canvasScale !== 1){this.canvas_ctx_.scale(canvasScale,canvasScale);}var hiddenScale=utils.getContextPixelRatio(this.hidden_ctx_);this.hidden_.width = this.width_ * hiddenScale;this.hidden_.height = this.height_ * hiddenScale;this.hidden_.style.width = this.width_ + "px"; // for IE -this.hidden_.style.height = this.height_ + "px"; // for IE -if(hiddenScale !== 1){this.hidden_ctx_.scale(hiddenScale,hiddenScale);}}; /** - * Detach DOM elements in the dygraph and null out all data references. - * Calling this when you're done with a dygraph can dramatically reduce memory - * usage. See, e.g., the tests/perf.html example. - */Dygraph.prototype.destroy = function(){this.canvas_ctx_.restore();this.hidden_ctx_.restore(); // Destroy any plugins, in the reverse order that they were registered. -for(var i=this.plugins_.length - 1;i >= 0;i--) {var p=this.plugins_.pop();if(p.plugin.destroy)p.plugin.destroy();}var removeRecursive=function removeRecursive(node){while(node.hasChildNodes()) {removeRecursive(node.firstChild);node.removeChild(node.firstChild);}};this.removeTrackedEvents_(); // remove mouse event handlers (This may not be necessary anymore) -utils.removeEvent(window,'mouseout',this.mouseOutHandler_);utils.removeEvent(this.mouseEventElement_,'mousemove',this.mouseMoveHandler_); // remove window handlers -utils.removeEvent(window,'resize',this.resizeHandler_);this.resizeHandler_ = null;removeRecursive(this.maindiv_);var nullOut=function nullOut(obj){for(var n in obj) {if(typeof obj[n] === 'object'){obj[n] = null;}}}; // These may not all be necessary, but it can't hurt... -nullOut(this.layout_);nullOut(this.plotter_);nullOut(this);}; /** - * Creates the canvas on which the chart will be drawn. Only the Renderer ever - * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots - * or the zoom rectangles) is done on this.canvas_. - * @param {Object} canvas The Dygraph canvas over which to overlay the plot - * @return {Object} The newly-created canvas - * @private - */Dygraph.prototype.createPlotKitCanvas_ = function(canvas){var h=utils.createCanvas();h.style.position = "absolute"; // TODO(danvk): h should be offset from canvas. canvas needs to include -// some extra area to make it easier to zoom in on the far left and far -// right. h needs to be precisely the plot area, so that clipping occurs. -h.style.top = canvas.style.top;h.style.left = canvas.style.left;h.width = this.width_;h.height = this.height_;h.style.width = this.width_ + "px"; // for IE -h.style.height = this.height_ + "px"; // for IE -return h;}; /** - * Creates an overlay element used to handle mouse events. - * @return {Object} The mouse event element. - * @private - */Dygraph.prototype.createMouseEventElement_ = function(){return this.canvas_;}; /** - * Generate a set of distinct colors for the data series. This is done with a - * color wheel. Saturation/Value are customizable, and the hue is - * equally-spaced around the color wheel. If a custom set of colors is - * specified, that is used instead. - * @private - */Dygraph.prototype.setColors_ = function(){var labels=this.getLabels();var num=labels.length - 1;this.colors_ = [];this.colorsMap_ = {}; // These are used for when no custom colors are specified. -var sat=this.getNumericOption('colorSaturation') || 1.0;var val=this.getNumericOption('colorValue') || 0.5;var half=Math.ceil(num / 2);var colors=this.getOption('colors');var visibility=this.visibility();for(var i=0;i < num;i++) {if(!visibility[i]){continue;}var label=labels[i + 1];var colorStr=this.attributes_.getForSeries('color',label);if(!colorStr){if(colors){colorStr = colors[i % colors.length];}else { // alternate colors for high contrast. -var idx=i % 2?half + (i + 1) / 2:Math.ceil((i + 1) / 2);var hue=1.0 * idx / (1 + num);colorStr = utils.hsvToRGB(hue,sat,val);}}this.colors_.push(colorStr);this.colorsMap_[label] = colorStr;}}; /** - * Return the list of colors. This is either the list of colors passed in the - * attributes or the autogenerated list of rgb(r,g,b) strings. - * This does not return colors for invisible series. - * @return {Array.} The list of colors. - */Dygraph.prototype.getColors = function(){return this.colors_;}; /** - * Returns a few attributes of a series, i.e. its color, its visibility, which - * axis it's assigned to, and its column in the original data. - * Returns null if the series does not exist. - * Otherwise, returns an object with column, visibility, color and axis properties. - * The "axis" property will be set to 1 for y1 and 2 for y2. - * The "column" property can be fed back into getValue(row, column) to get - * values for this series. - */Dygraph.prototype.getPropertiesForSeries = function(series_name){var idx=-1;var labels=this.getLabels();for(var i=1;i < labels.length;i++) {if(labels[i] == series_name){idx = i;break;}}if(idx == -1)return null;return {name:series_name,column:idx,visible:this.visibility()[idx - 1],color:this.colorsMap_[series_name],axis:1 + this.attributes_.axisForSeries(series_name)};}; /** - * Create the text box to adjust the averaging period - * @private - */Dygraph.prototype.createRollInterface_ = function(){ // Create a roller if one doesn't exist already. -if(!this.roller_){this.roller_ = document.createElement("input");this.roller_.type = "text";this.roller_.style.display = "none";this.graphDiv.appendChild(this.roller_);}var display=this.getBooleanOption('showRoller')?'block':'none';var area=this.plotter_.area;var textAttr={"position":"absolute","zIndex":10,"top":area.y + area.h - 25 + "px","left":area.x + 1 + "px","display":display};this.roller_.size = "2";this.roller_.value = this.rollPeriod_;for(var name in textAttr) {if(textAttr.hasOwnProperty(name)){this.roller_.style[name] = textAttr[name];}}var dygraph=this;this.roller_.onchange = function(){dygraph.adjustRoll(dygraph.roller_.value);};}; /** - * Set up all the mouse handlers needed to capture dragging behavior for zoom - * events. - * @private - */Dygraph.prototype.createDragInterface_ = function(){var context={ // Tracks whether the mouse is down right now -isZooming:false,isPanning:false, // is this drag part of a pan? -is2DPan:false, // if so, is that pan 1- or 2-dimensional? -dragStartX:null, // pixel coordinates -dragStartY:null, // pixel coordinates -dragEndX:null, // pixel coordinates -dragEndY:null, // pixel coordinates -dragDirection:null,prevEndX:null, // pixel coordinates -prevEndY:null, // pixel coordinates -prevDragDirection:null,cancelNextDblclick:false, // see comment in dygraph-interaction-model.js -// The value on the left side of the graph when a pan operation starts. -initialLeftmostDate:null, // The number of units each pixel spans. (This won't be valid for log -// scales) -xUnitsPerPixel:null, // TODO(danvk): update this comment -// The range in second/value units that the viewport encompasses during a -// panning operation. -dateRange:null, // Top-left corner of the canvas, in DOM coords -// TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY. -px:0,py:0, // Values for use with panEdgeFraction, which limit how far outside the -// graph's data boundaries it can be panned. -boundedDates:null, // [minDate, maxDate] -boundedValues:null, // [[minValue, maxValue] ...] -// We cover iframes during mouse interactions. See comments in -// dygraph-utils.js for more info on why this is a good idea. -tarp:new _iframeTarp2['default'](), // contextB is the same thing as this context object but renamed. -initializeMouseDown:function initializeMouseDown(event,g,contextB){ // prevents mouse drags from selecting page text. -if(event.preventDefault){event.preventDefault(); // Firefox, Chrome, etc. -}else {event.returnValue = false; // IE -event.cancelBubble = true;}var canvasPos=utils.findPos(g.canvas_);contextB.px = canvasPos.x;contextB.py = canvasPos.y;contextB.dragStartX = utils.dragGetX_(event,contextB);contextB.dragStartY = utils.dragGetY_(event,contextB);contextB.cancelNextDblclick = false;contextB.tarp.cover();},destroy:function destroy(){var context=this;if(context.isZooming || context.isPanning){context.isZooming = false;context.dragStartX = null;context.dragStartY = null;}if(context.isPanning){context.isPanning = false;context.draggingDate = null;context.dateRange = null;for(var i=0;i < self.axes_.length;i++) {delete self.axes_[i].draggingValue;delete self.axes_[i].dragValueRange;}}context.tarp.uncover();}};var interactionModel=this.getOption("interactionModel"); // Self is the graph. -var self=this; // Function that binds the graph and context to the handler. -var bindHandler=function bindHandler(handler){return function(event){handler(event,self,context);};};for(var eventName in interactionModel) {if(!interactionModel.hasOwnProperty(eventName))continue;this.addAndTrackEvent(this.mouseEventElement_,eventName,bindHandler(interactionModel[eventName]));} // If the user releases the mouse button during a drag, but not over the -// canvas, then it doesn't count as a zooming action. -if(!interactionModel.willDestroyContextMyself){var mouseUpHandler=function mouseUpHandler(event){context.destroy();};this.addAndTrackEvent(document,'mouseup',mouseUpHandler);}}; /** - * Draw a gray zoom rectangle over the desired area of the canvas. Also clears - * up any previous zoom rectangles that were drawn. This could be optimized to - * avoid extra redrawing, but it's tricky to avoid interactions with the status - * dots. - * - * @param {number} direction the direction of the zoom rectangle. Acceptable - * values are utils.HORIZONTAL and utils.VERTICAL. - * @param {number} startX The X position where the drag started, in canvas - * coordinates. - * @param {number} endX The current X position of the drag, in canvas coords. - * @param {number} startY The Y position where the drag started, in canvas - * coordinates. - * @param {number} endY The current Y position of the drag, in canvas coords. - * @param {number} prevDirection the value of direction on the previous call to - * this function. Used to avoid excess redrawing - * @param {number} prevEndX The value of endX on the previous call to this - * function. Used to avoid excess redrawing - * @param {number} prevEndY The value of endY on the previous call to this - * function. Used to avoid excess redrawing - * @private - */Dygraph.prototype.drawZoomRect_ = function(direction,startX,endX,startY,endY,prevDirection,prevEndX,prevEndY){var ctx=this.canvas_ctx_; // Clean up from the previous rect if necessary -if(prevDirection == utils.HORIZONTAL){ctx.clearRect(Math.min(startX,prevEndX),this.layout_.getPlotArea().y,Math.abs(startX - prevEndX),this.layout_.getPlotArea().h);}else if(prevDirection == utils.VERTICAL){ctx.clearRect(this.layout_.getPlotArea().x,Math.min(startY,prevEndY),this.layout_.getPlotArea().w,Math.abs(startY - prevEndY));} // Draw a light-grey rectangle to show the new viewing area -if(direction == utils.HORIZONTAL){if(endX && startX){ctx.fillStyle = "rgba(128,128,128,0.33)";ctx.fillRect(Math.min(startX,endX),this.layout_.getPlotArea().y,Math.abs(endX - startX),this.layout_.getPlotArea().h);}}else if(direction == utils.VERTICAL){if(endY && startY){ctx.fillStyle = "rgba(128,128,128,0.33)";ctx.fillRect(this.layout_.getPlotArea().x,Math.min(startY,endY),this.layout_.getPlotArea().w,Math.abs(endY - startY));}}}; /** - * Clear the zoom rectangle (and perform no zoom). - * @private - */Dygraph.prototype.clearZoomRect_ = function(){this.currentZoomRectArgs_ = null;this.canvas_ctx_.clearRect(0,0,this.width_,this.height_);}; /** - * Zoom to something containing [lowX, highX]. These are pixel coordinates in - * the canvas. The exact zoom window may be slightly larger if there are no data - * points near lowX or highX. Don't confuse this function with doZoomXDates, - * which accepts dates that match the raw data. This function redraws the graph. - * - * @param {number} lowX The leftmost pixel value that should be visible. - * @param {number} highX The rightmost pixel value that should be visible. - * @private - */Dygraph.prototype.doZoomX_ = function(lowX,highX){this.currentZoomRectArgs_ = null; // Find the earliest and latest dates contained in this canvasx range. -// Convert the call to date ranges of the raw data. -var minDate=this.toDataXCoord(lowX);var maxDate=this.toDataXCoord(highX);this.doZoomXDates_(minDate,maxDate);}; /** - * Zoom to something containing [minDate, maxDate] values. Don't confuse this - * method with doZoomX which accepts pixel coordinates. This function redraws - * the graph. - * - * @param {number} minDate The minimum date that should be visible. - * @param {number} maxDate The maximum date that should be visible. - * @private - */Dygraph.prototype.doZoomXDates_ = function(minDate,maxDate){ // TODO(danvk): when xAxisRange is null (i.e. "fit to data", the animation -// can produce strange effects. Rather than the x-axis transitioning slowly -// between values, it can jerk around.) -var old_window=this.xAxisRange();var new_window=[minDate,maxDate];this.zoomed_x_ = true;var that=this;this.doAnimatedZoom(old_window,new_window,null,null,function(){if(that.getFunctionOption("zoomCallback")){that.getFunctionOption("zoomCallback").call(that,minDate,maxDate,that.yAxisRanges());}});}; /** - * Zoom to something containing [lowY, highY]. These are pixel coordinates in - * the canvas. This function redraws the graph. - * - * @param {number} lowY The topmost pixel value that should be visible. - * @param {number} highY The lowest pixel value that should be visible. - * @private - */Dygraph.prototype.doZoomY_ = function(lowY,highY){this.currentZoomRectArgs_ = null; // Find the highest and lowest values in pixel range for each axis. -// Note that lowY (in pixels) corresponds to the max Value (in data coords). -// This is because pixels increase as you go down on the screen, whereas data -// coordinates increase as you go up the screen. -var oldValueRanges=this.yAxisRanges();var newValueRanges=[];for(var i=0;i < this.axes_.length;i++) {var hi=this.toDataYCoord(lowY,i);var low=this.toDataYCoord(highY,i);newValueRanges.push([low,hi]);}this.zoomed_y_ = true;var that=this;this.doAnimatedZoom(null,null,oldValueRanges,newValueRanges,function(){if(that.getFunctionOption("zoomCallback")){var xRange=that.xAxisRange();that.getFunctionOption("zoomCallback").call(that,xRange[0],xRange[1],that.yAxisRanges());}});}; /** - * Transition function to use in animations. Returns values between 0.0 - * (totally old values) and 1.0 (totally new values) for each frame. - * @private - */Dygraph.zoomAnimationFunction = function(frame,numFrames){var k=1.5;return (1.0 - Math.pow(k,-frame)) / (1.0 - Math.pow(k,-numFrames));}; /** - * Reset the zoom to the original view coordinates. This is the same as - * double-clicking on the graph. - */Dygraph.prototype.resetZoom = function(){var dirty=false,dirtyX=false,dirtyY=false;if(this.dateWindow_ !== null){dirty = true;dirtyX = true;}for(var i=0;i < this.axes_.length;i++) {if(typeof this.axes_[i].valueWindow !== 'undefined' && this.axes_[i].valueWindow !== null){dirty = true;dirtyY = true;}} // Clear any selection, since it's likely to be drawn in the wrong place. -this.clearSelection();if(dirty){this.zoomed_x_ = false;this.zoomed_y_ = false; //calculate extremes to avoid lack of padding on reset. -var extremes=this.xAxisExtremes();var minDate=extremes[0],maxDate=extremes[1]; // TODO(danvk): merge this block w/ the code below. -if(!this.getBooleanOption("animatedZooms")){this.dateWindow_ = null;for(i = 0;i < this.axes_.length;i++) {if(this.axes_[i].valueWindow !== null){delete this.axes_[i].valueWindow;}}this.drawGraph_();if(this.getFunctionOption("zoomCallback")){this.getFunctionOption("zoomCallback").call(this,minDate,maxDate,this.yAxisRanges());}return;}var oldWindow=null,newWindow=null,oldValueRanges=null,newValueRanges=null;if(dirtyX){oldWindow = this.xAxisRange();newWindow = [minDate,maxDate];}if(dirtyY){oldValueRanges = this.yAxisRanges(); // TODO(danvk): this is pretty inefficient -var packed=this.gatherDatasets_(this.rolledSeries_,null);var extremes=packed.extremes; // this has the side-effect of modifying this.axes_. -// this doesn't make much sense in this context, but it's convenient (we -// need this.axes_[*].extremeValues) and not harmful since we'll be -// calling drawGraph_ shortly, which clobbers these values. -this.computeYAxisRanges_(extremes);newValueRanges = [];for(i = 0;i < this.axes_.length;i++) {var axis=this.axes_[i];newValueRanges.push(axis.valueRange !== null && axis.valueRange !== undefined?axis.valueRange:axis.extremeRange);}}var that=this;this.doAnimatedZoom(oldWindow,newWindow,oldValueRanges,newValueRanges,function(){that.dateWindow_ = null;for(var i=0;i < that.axes_.length;i++) {if(that.axes_[i].valueWindow !== null){delete that.axes_[i].valueWindow;}}if(that.getFunctionOption("zoomCallback")){that.getFunctionOption("zoomCallback").call(that,minDate,maxDate,that.yAxisRanges());}});}}; /** - * Combined animation logic for all zoom functions. - * either the x parameters or y parameters may be null. - * @private - */Dygraph.prototype.doAnimatedZoom = function(oldXRange,newXRange,oldYRanges,newYRanges,callback){var steps=this.getBooleanOption("animatedZooms")?Dygraph.ANIMATION_STEPS:1;var windows=[];var valueRanges=[];var step,frac;if(oldXRange !== null && newXRange !== null){for(step = 1;step <= steps;step++) {frac = Dygraph.zoomAnimationFunction(step,steps);windows[step - 1] = [oldXRange[0] * (1 - frac) + frac * newXRange[0],oldXRange[1] * (1 - frac) + frac * newXRange[1]];}}if(oldYRanges !== null && newYRanges !== null){for(step = 1;step <= steps;step++) {frac = Dygraph.zoomAnimationFunction(step,steps);var thisRange=[];for(var j=0;j < this.axes_.length;j++) {thisRange.push([oldYRanges[j][0] * (1 - frac) + frac * newYRanges[j][0],oldYRanges[j][1] * (1 - frac) + frac * newYRanges[j][1]]);}valueRanges[step - 1] = thisRange;}}var that=this;utils.repeatAndCleanup(function(step){if(valueRanges.length){for(var i=0;i < that.axes_.length;i++) {var w=valueRanges[step][i];that.axes_[i].valueWindow = [w[0],w[1]];}}if(windows.length){that.dateWindow_ = windows[step];}that.drawGraph_();},steps,Dygraph.ANIMATION_DURATION / steps,callback);}; /** - * Get the current graph's area object. - * - * Returns: {x, y, w, h} - */Dygraph.prototype.getArea = function(){return this.plotter_.area;}; /** - * Convert a mouse event to DOM coordinates relative to the graph origin. - * - * Returns a two-element array: [X, Y]. - */Dygraph.prototype.eventToDomCoords = function(event){if(event.offsetX && event.offsetY){return [event.offsetX,event.offsetY];}else {var eventElementPos=utils.findPos(this.mouseEventElement_);var canvasx=utils.pageX(event) - eventElementPos.x;var canvasy=utils.pageY(event) - eventElementPos.y;return [canvasx,canvasy];}}; /** - * Given a canvas X coordinate, find the closest row. - * @param {number} domX graph-relative DOM X coordinate - * Returns {number} row number. - * @private - */Dygraph.prototype.findClosestRow = function(domX){var minDistX=Infinity;var closestRow=-1;var sets=this.layout_.points;for(var i=0;i < sets.length;i++) {var points=sets[i];var len=points.length;for(var j=0;j < len;j++) {var point=points[j];if(!utils.isValidPoint(point,true))continue;var dist=Math.abs(point.canvasx - domX);if(dist < minDistX){minDistX = dist;closestRow = point.idx;}}}return closestRow;}; /** - * Given canvas X,Y coordinates, find the closest point. - * - * This finds the individual data point across all visible series - * that's closest to the supplied DOM coordinates using the standard - * Euclidean X,Y distance. - * - * @param {number} domX graph-relative DOM X coordinate - * @param {number} domY graph-relative DOM Y coordinate - * Returns: {row, seriesName, point} - * @private - */Dygraph.prototype.findClosestPoint = function(domX,domY){var minDist=Infinity;var dist,dx,dy,point,closestPoint,closestSeries,closestRow;for(var setIdx=this.layout_.points.length - 1;setIdx >= 0;--setIdx) {var points=this.layout_.points[setIdx];for(var i=0;i < points.length;++i) {point = points[i];if(!utils.isValidPoint(point))continue;dx = point.canvasx - domX;dy = point.canvasy - domY;dist = dx * dx + dy * dy;if(dist < minDist){minDist = dist;closestPoint = point;closestSeries = setIdx;closestRow = point.idx;}}}var name=this.layout_.setNames[closestSeries];return {row:closestRow,seriesName:name,point:closestPoint};}; /** - * Given canvas X,Y coordinates, find the touched area in a stacked graph. - * - * This first finds the X data point closest to the supplied DOM X coordinate, - * then finds the series which puts the Y coordinate on top of its filled area, - * using linear interpolation between adjacent point pairs. - * - * @param {number} domX graph-relative DOM X coordinate - * @param {number} domY graph-relative DOM Y coordinate - * Returns: {row, seriesName, point} - * @private - */Dygraph.prototype.findStackedPoint = function(domX,domY){var row=this.findClosestRow(domX);var closestPoint,closestSeries;for(var setIdx=0;setIdx < this.layout_.points.length;++setIdx) {var boundary=this.getLeftBoundary_(setIdx);var rowIdx=row - boundary;var points=this.layout_.points[setIdx];if(rowIdx >= points.length)continue;var p1=points[rowIdx];if(!utils.isValidPoint(p1))continue;var py=p1.canvasy;if(domX > p1.canvasx && rowIdx + 1 < points.length){ // interpolate series Y value using next point -var p2=points[rowIdx + 1];if(utils.isValidPoint(p2)){var dx=p2.canvasx - p1.canvasx;if(dx > 0){var r=(domX - p1.canvasx) / dx;py += r * (p2.canvasy - p1.canvasy);}}}else if(domX < p1.canvasx && rowIdx > 0){ // interpolate series Y value using previous point -var p0=points[rowIdx - 1];if(utils.isValidPoint(p0)){var dx=p1.canvasx - p0.canvasx;if(dx > 0){var r=(p1.canvasx - domX) / dx;py += r * (p0.canvasy - p1.canvasy);}}} // Stop if the point (domX, py) is above this series' upper edge -if(setIdx === 0 || py < domY){closestPoint = p1;closestSeries = setIdx;}}var name=this.layout_.setNames[closestSeries];return {row:row,seriesName:name,point:closestPoint};}; /** - * When the mouse moves in the canvas, display information about a nearby data - * point and draw dots over those points in the data series. This function - * takes care of cleanup of previously-drawn dots. - * @param {Object} event The mousemove event from the browser. - * @private - */Dygraph.prototype.mouseMove_ = function(event){ // This prevents JS errors when mousing over the canvas before data loads. -var points=this.layout_.points;if(points === undefined || points === null)return;var canvasCoords=this.eventToDomCoords(event);var canvasx=canvasCoords[0];var canvasy=canvasCoords[1];var highlightSeriesOpts=this.getOption("highlightSeriesOpts");var selectionChanged=false;if(highlightSeriesOpts && !this.isSeriesLocked()){var closest;if(this.getBooleanOption("stackedGraph")){closest = this.findStackedPoint(canvasx,canvasy);}else {closest = this.findClosestPoint(canvasx,canvasy);}selectionChanged = this.setSelection(closest.row,closest.seriesName);}else {var idx=this.findClosestRow(canvasx);selectionChanged = this.setSelection(idx);}var callback=this.getFunctionOption("highlightCallback");if(callback && selectionChanged){callback.call(this,event,this.lastx_,this.selPoints_,this.lastRow_,this.highlightSet_);}}; /** - * Fetch left offset from the specified set index or if not passed, the - * first defined boundaryIds record (see bug #236). - * @private - */Dygraph.prototype.getLeftBoundary_ = function(setIdx){if(this.boundaryIds_[setIdx]){return this.boundaryIds_[setIdx][0];}else {for(var i=0;i < this.boundaryIds_.length;i++) {if(this.boundaryIds_[i] !== undefined){return this.boundaryIds_[i][0];}}return 0;}};Dygraph.prototype.animateSelection_ = function(direction){var totalSteps=10;var millis=30;if(this.fadeLevel === undefined)this.fadeLevel = 0;if(this.animateId === undefined)this.animateId = 0;var start=this.fadeLevel;var steps=direction < 0?start:totalSteps - start;if(steps <= 0){if(this.fadeLevel){this.updateSelection_(1.0);}return;}var thisId=++this.animateId;var that=this;var cleanupIfClearing=function cleanupIfClearing(){ // if we haven't reached fadeLevel 0 in the max frame time, -// ensure that the clear happens and just go to 0 -if(that.fadeLevel !== 0 && direction < 0){that.fadeLevel = 0;that.clearSelection();}};utils.repeatAndCleanup(function(n){ // ignore simultaneous animations -if(that.animateId != thisId)return;that.fadeLevel += direction;if(that.fadeLevel === 0){that.clearSelection();}else {that.updateSelection_(that.fadeLevel / totalSteps);}},steps,millis,cleanupIfClearing);}; /** - * Draw dots over the selectied points in the data series. This function - * takes care of cleanup of previously-drawn dots. - * @private - */Dygraph.prototype.updateSelection_ = function(opt_animFraction){ /*var defaultPrevented = */this.cascadeEvents_('select',{selectedRow:this.lastRow_,selectedX:this.lastx_,selectedPoints:this.selPoints_}); // TODO(danvk): use defaultPrevented here? -// Clear the previously drawn vertical, if there is one -var i;var ctx=this.canvas_ctx_;if(this.getOption('highlightSeriesOpts')){ctx.clearRect(0,0,this.width_,this.height_);var alpha=1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha');var backgroundColor=utils.toRGB_(this.getOption('highlightSeriesBackgroundColor'));if(alpha){ // Activating background fade includes an animation effect for a gradual -// fade. TODO(klausw): make this independently configurable if it causes -// issues? Use a shared preference to control animations? -var animateBackgroundFade=true;if(animateBackgroundFade){if(opt_animFraction === undefined){ // start a new animation -this.animateSelection_(1);return;}alpha *= opt_animFraction;}ctx.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + alpha + ')';ctx.fillRect(0,0,this.width_,this.height_);} // Redraw only the highlighted series in the interactive canvas (not the -// static plot canvas, which is where series are usually drawn). -this.plotter_._renderLineChart(this.highlightSet_,ctx);}else if(this.previousVerticalX_ >= 0){ // Determine the maximum highlight circle size. -var maxCircleSize=0;var labels=this.attr_('labels');for(i = 1;i < labels.length;i++) {var r=this.getNumericOption('highlightCircleSize',labels[i]);if(r > maxCircleSize)maxCircleSize = r;}var px=this.previousVerticalX_;ctx.clearRect(px - maxCircleSize - 1,0,2 * maxCircleSize + 2,this.height_);}if(this.selPoints_.length > 0){ // Draw colored circles over the center of each selected point -var canvasx=this.selPoints_[0].canvasx;ctx.save();for(i = 0;i < this.selPoints_.length;i++) {var pt=this.selPoints_[i];if(isNaN(pt.canvasy))continue;var circleSize=this.getNumericOption('highlightCircleSize',pt.name);var callback=this.getFunctionOption("drawHighlightPointCallback",pt.name);var color=this.plotter_.colors[pt.name];if(!callback){callback = utils.Circles.DEFAULT;}ctx.lineWidth = this.getNumericOption('strokeWidth',pt.name);ctx.strokeStyle = color;ctx.fillStyle = color;callback.call(this,this,pt.name,ctx,canvasx,pt.canvasy,color,circleSize,pt.idx);}ctx.restore();this.previousVerticalX_ = canvasx;}}; /** - * Manually set the selected points and display information about them in the - * legend. The selection can be cleared using clearSelection() and queried - * using getSelection(). - * @param {number} row Row number that should be highlighted (i.e. appear with - * hover dots on the chart). - * @param {seriesName} optional series name to highlight that series with the - * the highlightSeriesOpts setting. - * @param { locked } optional If true, keep seriesName selected when mousing - * over the graph, disabling closest-series highlighting. Call clearSelection() - * to unlock it. - */Dygraph.prototype.setSelection = function(row,opt_seriesName,opt_locked){ // Extract the points we've selected -this.selPoints_ = [];var changed=false;if(row !== false && row >= 0){if(row != this.lastRow_)changed = true;this.lastRow_ = row;for(var setIdx=0;setIdx < this.layout_.points.length;++setIdx) {var points=this.layout_.points[setIdx]; // Check if the point at the appropriate index is the point we're looking -// for. If it is, just use it, otherwise search the array for a point -// in the proper place. -var setRow=row - this.getLeftBoundary_(setIdx);if(setRow < points.length && points[setRow].idx == row){var point=points[setRow];if(point.yval !== null)this.selPoints_.push(point);}else {for(var pointIdx=0;pointIdx < points.length;++pointIdx) {var point=points[pointIdx];if(point.idx == row){if(point.yval !== null){this.selPoints_.push(point);}break;}}}}}else {if(this.lastRow_ >= 0)changed = true;this.lastRow_ = -1;}if(this.selPoints_.length){this.lastx_ = this.selPoints_[0].xval;}else {this.lastx_ = -1;}if(opt_seriesName !== undefined){if(this.highlightSet_ !== opt_seriesName)changed = true;this.highlightSet_ = opt_seriesName;}if(opt_locked !== undefined){this.lockedSet_ = opt_locked;}if(changed){this.updateSelection_(undefined);}return changed;}; /** - * The mouse has left the canvas. Clear out whatever artifacts remain - * @param {Object} event the mouseout event from the browser. - * @private - */Dygraph.prototype.mouseOut_ = function(event){if(this.getFunctionOption("unhighlightCallback")){this.getFunctionOption("unhighlightCallback").call(this,event);}if(this.getBooleanOption("hideOverlayOnMouseOut") && !this.lockedSet_){this.clearSelection();}}; /** - * Clears the current selection (i.e. points that were highlighted by moving - * the mouse over the chart). - */Dygraph.prototype.clearSelection = function(){this.cascadeEvents_('deselect',{});this.lockedSet_ = false; // Get rid of the overlay data -if(this.fadeLevel){this.animateSelection_(-1);return;}this.canvas_ctx_.clearRect(0,0,this.width_,this.height_);this.fadeLevel = 0;this.selPoints_ = [];this.lastx_ = -1;this.lastRow_ = -1;this.highlightSet_ = null;}; /** - * Returns the number of the currently selected row. To get data for this row, - * you can use the getValue method. - * @return {number} row number, or -1 if nothing is selected - */Dygraph.prototype.getSelection = function(){if(!this.selPoints_ || this.selPoints_.length < 1){return -1;}for(var setIdx=0;setIdx < this.layout_.points.length;setIdx++) {var points=this.layout_.points[setIdx];for(var row=0;row < points.length;row++) {if(points[row].x == this.selPoints_[0].x){return points[row].idx;}}}return -1;}; /** - * Returns the name of the currently-highlighted series. - * Only available when the highlightSeriesOpts option is in use. - */Dygraph.prototype.getHighlightSeries = function(){return this.highlightSet_;}; /** - * Returns true if the currently-highlighted series was locked - * via setSelection(..., seriesName, true). - */Dygraph.prototype.isSeriesLocked = function(){return this.lockedSet_;}; /** - * Fires when there's data available to be graphed. - * @param {string} data Raw CSV data to be plotted - * @private - */Dygraph.prototype.loadedEvent_ = function(data){this.rawData_ = this.parseCSV_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}; /** - * Add ticks on the x-axis representing years, months, quarters, weeks, or days - * @private - */Dygraph.prototype.addXTicks_ = function(){ // Determine the correct ticks scale on the x-axis: quarterly, monthly, ... -var range;if(this.dateWindow_){range = [this.dateWindow_[0],this.dateWindow_[1]];}else {range = this.xAxisExtremes();}var xAxisOptionsView=this.optionsViewForAxis_('x');var xTicks=xAxisOptionsView('ticker')(range[0],range[1],this.plotter_.area.w, // TODO(danvk): should be area.width -xAxisOptionsView,this); // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks); -// console.log(msg); -this.layout_.setXTicks(xTicks);}; /** - * Returns the correct handler class for the currently set options. - * @private - */Dygraph.prototype.getHandlerClass_ = function(){var handlerClass;if(this.attr_('dataHandler')){handlerClass = this.attr_('dataHandler');}else if(this.fractions_){if(this.getBooleanOption('errorBars')){handlerClass = _datahandlerBarsFractions2['default'];}else {handlerClass = _datahandlerDefaultFractions2['default'];}}else if(this.getBooleanOption('customBars')){handlerClass = _datahandlerBarsCustom2['default'];}else if(this.getBooleanOption('errorBars')){handlerClass = _datahandlerBarsError2['default'];}else {handlerClass = _datahandlerDefault2['default'];}return handlerClass;}; /** - * @private - * This function is called once when the chart's data is changed or the options - * dictionary is updated. It is _not_ called when the user pans or zooms. The - * idea is that values derived from the chart's data can be computed here, - * rather than every time the chart is drawn. This includes things like the - * number of axes, rolling averages, etc. - */Dygraph.prototype.predraw_ = function(){var start=new Date(); // Create the correct dataHandler -this.dataHandler_ = new (this.getHandlerClass_())();this.layout_.computePlotArea(); // TODO(danvk): move more computations out of drawGraph_ and into here. -this.computeYAxes_();if(!this.is_initial_draw_){this.canvas_ctx_.restore();this.hidden_ctx_.restore();}this.canvas_ctx_.save();this.hidden_ctx_.save(); // Create a new plotter. -this.plotter_ = new _dygraphCanvas2['default'](this,this.hidden_,this.hidden_ctx_,this.layout_); // The roller sits in the bottom left corner of the chart. We don't know where -// this will be until the options are available, so it's positioned here. -this.createRollInterface_();this.cascadeEvents_('predraw'); // Convert the raw data (a 2D array) into the internal format and compute -// rolling averages. -this.rolledSeries_ = [null]; // x-axis is the first series and it's special -for(var i=1;i < this.numColumns();i++) { // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too. -var series=this.dataHandler_.extractSeries(this.rawData_,i,this.attributes_);if(this.rollPeriod_ > 1){series = this.dataHandler_.rollingAverage(series,this.rollPeriod_,this.attributes_);}this.rolledSeries_.push(series);} // If the data or options have changed, then we'd better redraw. -this.drawGraph_(); // This is used to determine whether to do various animations. -var end=new Date();this.drawingTimeMs_ = end - start;}; /** - * Point structure. - * - * xval_* and yval_* are the original unscaled data values, - * while x_* and y_* are scaled to the range (0.0-1.0) for plotting. - * yval_stacked is the cumulative Y value used for stacking graphs, - * and bottom/top/minus/plus are used for error bar graphs. - * - * @typedef {{ - * idx: number, - * name: string, - * x: ?number, - * xval: ?number, - * y_bottom: ?number, - * y: ?number, - * y_stacked: ?number, - * y_top: ?number, - * yval_minus: ?number, - * yval: ?number, - * yval_plus: ?number, - * yval_stacked - * }} - */Dygraph.PointType = undefined; /** - * Calculates point stacking for stackedGraph=true. - * - * For stacking purposes, interpolate or extend neighboring data across - * NaN values based on stackedGraphNaNFill settings. This is for display - * only, the underlying data value as shown in the legend remains NaN. - * - * @param {Array.} points Point array for a single series. - * Updates each Point's yval_stacked property. - * @param {Array.} cumulativeYval Accumulated top-of-graph stacked Y - * values for the series seen so far. Index is the row number. Updated - * based on the current series's values. - * @param {Array.} seriesExtremes Min and max values, updated - * to reflect the stacked values. - * @param {string} fillMethod Interpolation method, one of 'all', 'inside', or - * 'none'. - * @private - */Dygraph.stackPoints_ = function(points,cumulativeYval,seriesExtremes,fillMethod){var lastXval=null;var prevPoint=null;var nextPoint=null;var nextPointIdx=-1; // Find the next stackable point starting from the given index. -var updateNextPoint=function updateNextPoint(idx){ // If we've previously found a non-NaN point and haven't gone past it yet, -// just use that. -if(nextPointIdx >= idx)return; // We haven't found a non-NaN point yet or have moved past it, -// look towards the right to find a non-NaN point. -for(var j=idx;j < points.length;++j) { // Clear out a previously-found point (if any) since it's no longer -// valid, we shouldn't use it for interpolation anymore. -nextPoint = null;if(!isNaN(points[j].yval) && points[j].yval !== null){nextPointIdx = j;nextPoint = points[j];break;}}};for(var i=0;i < points.length;++i) {var point=points[i];var xval=point.xval;if(cumulativeYval[xval] === undefined){cumulativeYval[xval] = 0;}var actualYval=point.yval;if(isNaN(actualYval) || actualYval === null){if(fillMethod == 'none'){actualYval = 0;}else { // Interpolate/extend for stacking purposes if possible. -updateNextPoint(i);if(prevPoint && nextPoint && fillMethod != 'none'){ // Use linear interpolation between prevPoint and nextPoint. -actualYval = prevPoint.yval + (nextPoint.yval - prevPoint.yval) * ((xval - prevPoint.xval) / (nextPoint.xval - prevPoint.xval));}else if(prevPoint && fillMethod == 'all'){actualYval = prevPoint.yval;}else if(nextPoint && fillMethod == 'all'){actualYval = nextPoint.yval;}else {actualYval = 0;}}}else {prevPoint = point;}var stackedYval=cumulativeYval[xval];if(lastXval != xval){ // If an x-value is repeated, we ignore the duplicates. -stackedYval += actualYval;cumulativeYval[xval] = stackedYval;}lastXval = xval;point.yval_stacked = stackedYval;if(stackedYval > seriesExtremes[1]){seriesExtremes[1] = stackedYval;}if(stackedYval < seriesExtremes[0]){seriesExtremes[0] = stackedYval;}}}; /** - * Loop over all fields and create datasets, calculating extreme y-values for - * each series and extreme x-indices as we go. - * - * dateWindow is passed in as an explicit parameter so that we can compute - * extreme values "speculatively", i.e. without actually setting state on the - * dygraph. - * - * @param {Array.)>>} rolledSeries, where - * rolledSeries[seriesIndex][row] = raw point, where - * seriesIndex is the column number starting with 1, and - * rawPoint is [x,y] or [x, [y, err]] or [x, [y, yminus, yplus]]. - * @param {?Array.} dateWindow [xmin, xmax] pair, or null. - * @return {{ - * points: Array.>, - * seriesExtremes: Array.>, - * boundaryIds: Array.}} - * @private - */Dygraph.prototype.gatherDatasets_ = function(rolledSeries,dateWindow){var boundaryIds=[];var points=[];var cumulativeYval=[]; // For stacked series. -var extremes={}; // series name -> [low, high] -var seriesIdx,sampleIdx;var firstIdx,lastIdx;var axisIdx; // Loop over the fields (series). Go from the last to the first, -// because if they're stacked that's how we accumulate the values. -var num_series=rolledSeries.length - 1;var series;for(seriesIdx = num_series;seriesIdx >= 1;seriesIdx--) {if(!this.visibility()[seriesIdx - 1])continue; // Prune down to the desired range, if necessary (for zooming) -// Because there can be lines going to points outside of the visible area, -// we actually prune to visible points, plus one on either side. -if(dateWindow){series = rolledSeries[seriesIdx];var low=dateWindow[0];var high=dateWindow[1]; // TODO(danvk): do binary search instead of linear search. -// TODO(danvk): pass firstIdx and lastIdx directly to the renderer. -firstIdx = null;lastIdx = null;for(sampleIdx = 0;sampleIdx < series.length;sampleIdx++) {if(series[sampleIdx][0] >= low && firstIdx === null){firstIdx = sampleIdx;}if(series[sampleIdx][0] <= high){lastIdx = sampleIdx;}}if(firstIdx === null)firstIdx = 0;var correctedFirstIdx=firstIdx;var isInvalidValue=true;while(isInvalidValue && correctedFirstIdx > 0) {correctedFirstIdx--; // check if the y value is null. -isInvalidValue = series[correctedFirstIdx][1] === null;}if(lastIdx === null)lastIdx = series.length - 1;var correctedLastIdx=lastIdx;isInvalidValue = true;while(isInvalidValue && correctedLastIdx < series.length - 1) {correctedLastIdx++;isInvalidValue = series[correctedLastIdx][1] === null;}if(correctedFirstIdx !== firstIdx){firstIdx = correctedFirstIdx;}if(correctedLastIdx !== lastIdx){lastIdx = correctedLastIdx;}boundaryIds[seriesIdx - 1] = [firstIdx,lastIdx]; // .slice's end is exclusive, we want to include lastIdx. -series = series.slice(firstIdx,lastIdx + 1);}else {series = rolledSeries[seriesIdx];boundaryIds[seriesIdx - 1] = [0,series.length - 1];}var seriesName=this.attr_("labels")[seriesIdx];var seriesExtremes=this.dataHandler_.getExtremeYValues(series,dateWindow,this.getBooleanOption("stepPlot",seriesName));var seriesPoints=this.dataHandler_.seriesToPoints(series,seriesName,boundaryIds[seriesIdx - 1][0]);if(this.getBooleanOption("stackedGraph")){axisIdx = this.attributes_.axisForSeries(seriesName);if(cumulativeYval[axisIdx] === undefined){cumulativeYval[axisIdx] = [];}Dygraph.stackPoints_(seriesPoints,cumulativeYval[axisIdx],seriesExtremes,this.getBooleanOption("stackedGraphNaNFill"));}extremes[seriesName] = seriesExtremes;points[seriesIdx] = seriesPoints;}return {points:points,extremes:extremes,boundaryIds:boundaryIds};}; /** - * Update the graph with new data. This method is called when the viewing area - * has changed. If the underlying data or options have changed, predraw_ will - * be called before drawGraph_ is called. - * - * @private - */Dygraph.prototype.drawGraph_ = function(){var start=new Date(); // This is used to set the second parameter to drawCallback, below. -var is_initial_draw=this.is_initial_draw_;this.is_initial_draw_ = false;this.layout_.removeAllDatasets();this.setColors_();this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize');var packed=this.gatherDatasets_(this.rolledSeries_,this.dateWindow_);var points=packed.points;var extremes=packed.extremes;this.boundaryIds_ = packed.boundaryIds;this.setIndexByName_ = {};var labels=this.attr_("labels");if(labels.length > 0){this.setIndexByName_[labels[0]] = 0;}var dataIdx=0;for(var i=1;i < points.length;i++) {this.setIndexByName_[labels[i]] = i;if(!this.visibility()[i - 1])continue;this.layout_.addDataset(labels[i],points[i]);this.datasetIndex_[i] = dataIdx++;}this.computeYAxisRanges_(extremes);this.layout_.setYAxes(this.axes_);this.addXTicks_(); // Save the X axis zoomed status as the updateOptions call will tend to set it erroneously -var tmp_zoomed_x=this.zoomed_x_; // Tell PlotKit to use this new data and render itself -this.zoomed_x_ = tmp_zoomed_x;this.layout_.evaluate();this.renderGraph_(is_initial_draw);if(this.getStringOption("timingName")){var end=new Date();console.log(this.getStringOption("timingName") + " - drawGraph: " + (end - start) + "ms");}}; /** - * This does the work of drawing the chart. It assumes that the layout and axis - * scales have already been set (e.g. by predraw_). - * - * @private - */Dygraph.prototype.renderGraph_ = function(is_initial_draw){this.cascadeEvents_('clearChart');this.plotter_.clear();if(this.getFunctionOption('underlayCallback')){ // NOTE: we pass the dygraph object to this callback twice to avoid breaking -// users who expect a deprecated form of this callback. -this.getFunctionOption('underlayCallback').call(this,this.hidden_ctx_,this.layout_.getPlotArea(),this,this);}var e={canvas:this.hidden_,drawingContext:this.hidden_ctx_};this.cascadeEvents_('willDrawChart',e);this.plotter_.render();this.cascadeEvents_('didDrawChart',e);this.lastRow_ = -1; // because plugins/legend.js clears the legend -// TODO(danvk): is this a performance bottleneck when panning? -// The interaction canvas should already be empty in that situation. -this.canvas_.getContext('2d').clearRect(0,0,this.width_,this.height_);if(this.getFunctionOption("drawCallback") !== null){this.getFunctionOption("drawCallback").call(this,this,is_initial_draw);}if(is_initial_draw){this.readyFired_ = true;while(this.readyFns_.length > 0) {var fn=this.readyFns_.pop();fn(this);}}}; /** - * @private - * Determine properties of the y-axes which are independent of the data - * currently being displayed. This includes things like the number of axes and - * the style of the axes. It does not include the range of each axis and its - * tick marks. - * This fills in this.axes_. - * axes_ = [ { options } ] - * indices are into the axes_ array. - */Dygraph.prototype.computeYAxes_ = function(){ // Preserve valueWindow settings if they exist, and if the user hasn't -// specified a new valueRange. -var valueWindows,axis,index,opts,v;if(this.axes_ !== undefined && this.user_attrs_.hasOwnProperty("valueRange") === false){valueWindows = [];for(index = 0;index < this.axes_.length;index++) {valueWindows.push(this.axes_[index].valueWindow);}} // this.axes_ doesn't match this.attributes_.axes_.options. It's used for -// data computation as well as options storage. -// Go through once and add all the axes. -this.axes_ = [];for(axis = 0;axis < this.attributes_.numAxes();axis++) { // Add a new axis, making a copy of its per-axis options. -opts = {g:this};utils.update(opts,this.attributes_.axisOptions(axis));this.axes_[axis] = opts;} // Copy global valueRange option over to the first axis. -// NOTE(konigsberg): Are these two statements necessary? -// I tried removing it. The automated tests pass, and manually -// messing with tests/zoom.html showed no trouble. -v = this.attr_('valueRange');if(v)this.axes_[0].valueRange = v;if(valueWindows !== undefined){ // Restore valueWindow settings. -// When going from two axes back to one, we only restore -// one axis. -var idxCount=Math.min(valueWindows.length,this.axes_.length);for(index = 0;index < idxCount;index++) {this.axes_[index].valueWindow = valueWindows[index];}}for(axis = 0;axis < this.axes_.length;axis++) {if(axis === 0){opts = this.optionsViewForAxis_('y' + (axis?'2':''));v = opts("valueRange");if(v)this.axes_[axis].valueRange = v;}else { // To keep old behavior -var axes=this.user_attrs_.axes;if(axes && axes.y2){v = axes.y2.valueRange;if(v)this.axes_[axis].valueRange = v;}}}}; /** - * Returns the number of y-axes on the chart. - * @return {number} the number of axes. - */Dygraph.prototype.numAxes = function(){return this.attributes_.numAxes();}; /** - * @private - * Returns axis properties for the given series. - * @param {string} setName The name of the series for which to get axis - * properties, e.g. 'Y1'. - * @return {Object} The axis properties. - */Dygraph.prototype.axisPropertiesForSeries = function(series){ // TODO(danvk): handle errors. -return this.axes_[this.attributes_.axisForSeries(series)];}; /** - * @private - * Determine the value range and tick marks for each axis. - * @param {Object} extremes A mapping from seriesName -> [low, high] - * This fills in the valueRange and ticks fields in each entry of this.axes_. - */Dygraph.prototype.computeYAxisRanges_ = function(extremes){var isNullUndefinedOrNaN=function isNullUndefinedOrNaN(num){return isNaN(parseFloat(num));};var numAxes=this.attributes_.numAxes();var ypadCompat,span,series,ypad;var p_axis; // Compute extreme values, a span and tick marks for each axis. -for(var i=0;i < numAxes;i++) {var axis=this.axes_[i];var logscale=this.attributes_.getForAxis("logscale",i);var includeZero=this.attributes_.getForAxis("includeZero",i);var independentTicks=this.attributes_.getForAxis("independentTicks",i);series = this.attributes_.seriesForAxis(i); // Add some padding. This supports two Y padding operation modes: -// -// - backwards compatible (yRangePad not set): -// 10% padding for automatic Y ranges, but not for user-supplied -// ranges, and move a close-to-zero edge to zero except if -// avoidMinZero is set, since drawing at the edge results in -// invisible lines. Unfortunately lines drawn at the edge of a -// user-supplied range will still be invisible. If logscale is -// set, add a variable amount of padding at the top but -// none at the bottom. -// -// - new-style (yRangePad set by the user): -// always add the specified Y padding. -// -ypadCompat = true;ypad = 0.1; // add 10% -if(this.getNumericOption('yRangePad') !== null){ypadCompat = false; // Convert pixel padding to ratio -ypad = this.getNumericOption('yRangePad') / this.plotter_.area.h;}if(series.length === 0){ // If no series are defined or visible then use a reasonable default -axis.extremeRange = [0,1];}else { // Calculate the extremes of extremes. -var minY=Infinity; // extremes[series[0]][0]; -var maxY=-Infinity; // extremes[series[0]][1]; -var extremeMinY,extremeMaxY;for(var j=0;j < series.length;j++) { // this skips invisible series -if(!extremes.hasOwnProperty(series[j]))continue; // Only use valid extremes to stop null data series' from corrupting the scale. -extremeMinY = extremes[series[j]][0];if(extremeMinY !== null){minY = Math.min(extremeMinY,minY);}extremeMaxY = extremes[series[j]][1];if(extremeMaxY !== null){maxY = Math.max(extremeMaxY,maxY);}} // Include zero if requested by the user. -if(includeZero && !logscale){if(minY > 0)minY = 0;if(maxY < 0)maxY = 0;} // Ensure we have a valid scale, otherwise default to [0, 1] for safety. -if(minY == Infinity)minY = 0;if(maxY == -Infinity)maxY = 1;span = maxY - minY; // special case: if we have no sense of scale, center on the sole value. -if(span === 0){if(maxY !== 0){span = Math.abs(maxY);}else { // ... and if the sole value is zero, use range 0-1. -maxY = 1;span = 1;}}var maxAxisY,minAxisY;if(logscale){if(ypadCompat){maxAxisY = maxY + ypad * span;minAxisY = minY;}else {var logpad=Math.exp(Math.log(span) * ypad);maxAxisY = maxY * logpad;minAxisY = minY / logpad;}}else {maxAxisY = maxY + ypad * span;minAxisY = minY - ypad * span; // Backwards-compatible behavior: Move the span to start or end at zero if it's -// close to zero, but not if avoidMinZero is set. -if(ypadCompat && !this.getBooleanOption("avoidMinZero")){if(minAxisY < 0 && minY >= 0)minAxisY = 0;if(maxAxisY > 0 && maxY <= 0)maxAxisY = 0;}}axis.extremeRange = [minAxisY,maxAxisY];}if(axis.valueWindow){ // This is only set if the user has zoomed on the y-axis. It is never set -// by a user. It takes precedence over axis.valueRange because, if you set -// valueRange, you'd still expect to be able to pan. -axis.computedValueRange = [axis.valueWindow[0],axis.valueWindow[1]];}else if(axis.valueRange){ // This is a user-set value range for this axis. -var y0=isNullUndefinedOrNaN(axis.valueRange[0])?axis.extremeRange[0]:axis.valueRange[0];var y1=isNullUndefinedOrNaN(axis.valueRange[1])?axis.extremeRange[1]:axis.valueRange[1];if(!ypadCompat){if(axis.logscale){var logpad=Math.exp(Math.log(span) * ypad);y0 *= logpad;y1 /= logpad;}else {span = y1 - y0;y0 -= span * ypad;y1 += span * ypad;}}axis.computedValueRange = [y0,y1];}else {axis.computedValueRange = axis.extremeRange;}if(independentTicks){axis.independentTicks = independentTicks;var opts=this.optionsViewForAxis_('y' + (i?'2':''));var ticker=opts('ticker');axis.ticks = ticker(axis.computedValueRange[0],axis.computedValueRange[1],this.plotter_.area.h,opts,this); // Define the first independent axis as primary axis. -if(!p_axis)p_axis = axis;}}if(p_axis === undefined){throw "Configuration Error: At least one axis has to have the \"independentTicks\" option activated.";} // Add ticks. By default, all axes inherit the tick positions of the -// primary axis. However, if an axis is specifically marked as having -// independent ticks, then that is permissible as well. -for(var i=0;i < numAxes;i++) {var axis=this.axes_[i];if(!axis.independentTicks){var opts=this.optionsViewForAxis_('y' + (i?'2':''));var ticker=opts('ticker');var p_ticks=p_axis.ticks;var p_scale=p_axis.computedValueRange[1] - p_axis.computedValueRange[0];var scale=axis.computedValueRange[1] - axis.computedValueRange[0];var tick_values=[];for(var k=0;k < p_ticks.length;k++) {var y_frac=(p_ticks[k].v - p_axis.computedValueRange[0]) / p_scale;var y_val=axis.computedValueRange[0] + y_frac * scale;tick_values.push(y_val);}axis.ticks = ticker(axis.computedValueRange[0],axis.computedValueRange[1],this.plotter_.area.h,opts,this,tick_values);}}}; /** - * Detects the type of the str (date or numeric) and sets the various - * formatting attributes in this.attrs_ based on this type. - * @param {string} str An x value. - * @private - */Dygraph.prototype.detectTypeFromString_ = function(str){var isDate=false;var dashPos=str.indexOf('-'); // could be 2006-01-01 _or_ 1.0e-2 -if(dashPos > 0 && str[dashPos - 1] != 'e' && str[dashPos - 1] != 'E' || str.indexOf('/') >= 0 || isNaN(parseFloat(str))){isDate = true;}else if(str.length == 8 && str > '19700101' && str < '20371231'){ // TODO(danvk): remove support for this format. -isDate = true;}this.setXAxisOptions_(isDate);};Dygraph.prototype.setXAxisOptions_ = function(isDate){if(isDate){this.attrs_.xValueParser = utils.dateParser;this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;}else { /** @private (shut up, jsdoc!) */this.attrs_.xValueParser = function(x){return parseFloat(x);}; // TODO(danvk): use Dygraph.numberValueFormatter here? -/** @private (shut up, jsdoc!) */this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;}}; /** - * @private - * Parses a string in a special csv format. We expect a csv file where each - * line is a date point, and the first field in each line is the date string. - * We also expect that all remaining fields represent series. - * if the errorBars attribute is set, then interpret the fields as: - * date, series1, stddev1, series2, stddev2, ... - * @param {[Object]} data See above. - * - * @return [Object] An array with one entry for each row. These entries - * are an array of cells in that row. The first entry is the parsed x-value for - * the row. The second, third, etc. are the y-values. These can take on one of - * three forms, depending on the CSV and constructor parameters: - * 1. numeric value - * 2. [ value, stddev ] - * 3. [ low value, center value, high value ] - */Dygraph.prototype.parseCSV_ = function(data){var ret=[];var line_delimiter=utils.detectLineDelimiter(data);var lines=data.split(line_delimiter || "\n");var vals,j; // Use the default delimiter or fall back to a tab if that makes sense. -var delim=this.getStringOption('delimiter');if(lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0){delim = '\t';}var start=0;if(!('labels' in this.user_attrs_)){ // User hasn't explicitly set labels, so they're (presumably) in the CSV. -start = 1;this.attrs_.labels = lines[0].split(delim); // NOTE: _not_ user_attrs_. -this.attributes_.reparseSeries();}var line_no=0;var xParser;var defaultParserSet=false; // attempt to auto-detect x value type -var expectedCols=this.attr_("labels").length;var outOfOrder=false;for(var i=start;i < lines.length;i++) {var line=lines[i];line_no = i;if(line.length === 0)continue; // skip blank lines -if(line[0] == '#')continue; // skip comment lines -var inFields=line.split(delim);if(inFields.length < 2)continue;var fields=[];if(!defaultParserSet){this.detectTypeFromString_(inFields[0]);xParser = this.getFunctionOption("xValueParser");defaultParserSet = true;}fields[0] = xParser(inFields[0],this); // If fractions are expected, parse the numbers as "A/B" -if(this.fractions_){for(j = 1;j < inFields.length;j++) { // TODO(danvk): figure out an appropriate way to flag parse errors. -vals = inFields[j].split("/");if(vals.length != 2){console.error('Expected fractional "num/den" values in CSV data ' + "but found a value '" + inFields[j] + "' on line " + (1 + i) + " ('" + line + "') which is not of this form.");fields[j] = [0,0];}else {fields[j] = [utils.parseFloat_(vals[0],i,line),utils.parseFloat_(vals[1],i,line)];}}}else if(this.getBooleanOption("errorBars")){ // If there are error bars, values are (value, stddev) pairs -if(inFields.length % 2 != 1){console.error('Expected alternating (value, stdev.) pairs in CSV data ' + 'but line ' + (1 + i) + ' has an odd number of values (' + (inFields.length - 1) + "): '" + line + "'");}for(j = 1;j < inFields.length;j += 2) {fields[(j + 1) / 2] = [utils.parseFloat_(inFields[j],i,line),utils.parseFloat_(inFields[j + 1],i,line)];}}else if(this.getBooleanOption("customBars")){ // Bars are a low;center;high tuple -for(j = 1;j < inFields.length;j++) {var val=inFields[j];if(/^ *$/.test(val)){fields[j] = [null,null,null];}else {vals = val.split(";");if(vals.length == 3){fields[j] = [utils.parseFloat_(vals[0],i,line),utils.parseFloat_(vals[1],i,line),utils.parseFloat_(vals[2],i,line)];}else {console.warn('When using customBars, values must be either blank ' + 'or "low;center;high" tuples (got "' + val + '" on line ' + (1 + i));}}}}else { // Values are just numbers -for(j = 1;j < inFields.length;j++) {fields[j] = utils.parseFloat_(inFields[j],i,line);}}if(ret.length > 0 && fields[0] < ret[ret.length - 1][0]){outOfOrder = true;}if(fields.length != expectedCols){console.error("Number of columns in line " + i + " (" + fields.length + ") does not agree with number of labels (" + expectedCols + ") " + line);} // If the user specified the 'labels' option and none of the cells of the -// first row parsed correctly, then they probably double-specified the -// labels. We go with the values set in the option, discard this row and -// log a warning to the JS console. -if(i === 0 && this.attr_('labels')){var all_null=true;for(j = 0;all_null && j < fields.length;j++) {if(fields[j])all_null = false;}if(all_null){console.warn("The dygraphs 'labels' option is set, but the first row " + "of CSV data ('" + line + "') appears to also contain " + "labels. Will drop the CSV labels and use the option " + "labels.");continue;}}ret.push(fields);}if(outOfOrder){console.warn("CSV is out of order; order it correctly to speed loading.");ret.sort(function(a,b){return a[0] - b[0];});}return ret;}; /** - * The user has provided their data as a pre-packaged JS array. If the x values - * are numeric, this is the same as dygraphs' internal format. If the x values - * are dates, we need to convert them from Date objects to ms since epoch. - * @param {!Array} data - * @return {Object} data with numeric x values. - * @private - */Dygraph.prototype.parseArray_ = function(data){ // Peek at the first x value to see if it's numeric. -if(data.length === 0){console.error("Can't plot empty data set");return null;}if(data[0].length === 0){console.error("Data set cannot contain an empty row");return null;}var i;if(this.attr_("labels") === null){console.warn("Using default labels. Set labels explicitly via 'labels' " + "in the options parameter");this.attrs_.labels = ["X"];for(i = 1;i < data[0].length;i++) {this.attrs_.labels.push("Y" + i); // Not user_attrs_. -}this.attributes_.reparseSeries();}else {var num_labels=this.attr_("labels");if(num_labels.length != data[0].length){console.error("Mismatch between number of labels (" + num_labels + ")" + " and number of columns in array (" + data[0].length + ")");return null;}}if(utils.isDateLike(data[0][0])){ // Some intelligent defaults for a date x-axis. -this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; // Assume they're all dates. -var parsedData=utils.clone(data);for(i = 0;i < data.length;i++) {if(parsedData[i].length === 0){console.error("Row " + (1 + i) + " of data is empty");return null;}if(parsedData[i][0] === null || typeof parsedData[i][0].getTime != 'function' || isNaN(parsedData[i][0].getTime())){console.error("x value in row " + (1 + i) + " is not a Date");return null;}parsedData[i][0] = parsedData[i][0].getTime();}return parsedData;}else { // Some intelligent defaults for a numeric x-axis. -/** @private (shut up, jsdoc!) */this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = utils.numberAxisLabelFormatter;return data;}}; /** - * Parses a DataTable object from gviz. - * The data is expected to have a first column that is either a date or a - * number. All subsequent columns must be numbers. If there is a clear mismatch - * between this.xValueParser_ and the type of the first column, it will be - * fixed. Fills out rawData_. - * @param {!google.visualization.DataTable} data See above. - * @private - */Dygraph.prototype.parseDataTable_ = function(data){var shortTextForAnnotationNum=function shortTextForAnnotationNum(num){ // converts [0-9]+ [A-Z][a-z]* -// example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab -// and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz -var shortText=String.fromCharCode(65 /* A */ + num % 26);num = Math.floor(num / 26);while(num > 0) {shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26) + shortText.toLowerCase();num = Math.floor((num - 1) / 26);}return shortText;};var cols=data.getNumberOfColumns();var rows=data.getNumberOfRows();var indepType=data.getColumnType(0);if(indepType == 'date' || indepType == 'datetime'){this.attrs_.xValueParser = utils.dateParser;this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;}else if(indepType == 'number'){this.attrs_.xValueParser = function(x){return parseFloat(x);};this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;}else {throw new Error("only 'date', 'datetime' and 'number' types are supported " + "for column 1 of DataTable input (Got '" + indepType + "')");} // Array of the column indices which contain data (and not annotations). -var colIdx=[];var annotationCols={}; // data index -> [annotation cols] -var hasAnnotations=false;var i,j;for(i = 1;i < cols;i++) {var type=data.getColumnType(i);if(type == 'number'){colIdx.push(i);}else if(type == 'string' && this.getBooleanOption('displayAnnotations')){ // This is OK -- it's an annotation column. -var dataIdx=colIdx[colIdx.length - 1];if(!annotationCols.hasOwnProperty(dataIdx)){annotationCols[dataIdx] = [i];}else {annotationCols[dataIdx].push(i);}hasAnnotations = true;}else {throw new Error("Only 'number' is supported as a dependent type with Gviz." + " 'string' is only supported if displayAnnotations is true");}} // Read column labels -// TODO(danvk): add support back for errorBars -var labels=[data.getColumnLabel(0)];for(i = 0;i < colIdx.length;i++) {labels.push(data.getColumnLabel(colIdx[i]));if(this.getBooleanOption("errorBars"))i += 1;}this.attrs_.labels = labels;cols = labels.length;var ret=[];var outOfOrder=false;var annotations=[];for(i = 0;i < rows;i++) {var row=[];if(typeof data.getValue(i,0) === 'undefined' || data.getValue(i,0) === null){console.warn("Ignoring row " + i + " of DataTable because of undefined or null first column.");continue;}if(indepType == 'date' || indepType == 'datetime'){row.push(data.getValue(i,0).getTime());}else {row.push(data.getValue(i,0));}if(!this.getBooleanOption("errorBars")){for(j = 0;j < colIdx.length;j++) {var col=colIdx[j];row.push(data.getValue(i,col));if(hasAnnotations && annotationCols.hasOwnProperty(col) && data.getValue(i,annotationCols[col][0]) !== null){var ann={};ann.series = data.getColumnLabel(col);ann.xval = row[0];ann.shortText = shortTextForAnnotationNum(annotations.length);ann.text = '';for(var k=0;k < annotationCols[col].length;k++) {if(k)ann.text += "\n";ann.text += data.getValue(i,annotationCols[col][k]);}annotations.push(ann);}} // Strip out infinities, which give dygraphs problems later on. -for(j = 0;j < row.length;j++) {if(!isFinite(row[j]))row[j] = null;}}else {for(j = 0;j < cols - 1;j++) {row.push([data.getValue(i,1 + 2 * j),data.getValue(i,2 + 2 * j)]);}}if(ret.length > 0 && row[0] < ret[ret.length - 1][0]){outOfOrder = true;}ret.push(row);}if(outOfOrder){console.warn("DataTable is out of order; order it correctly to speed loading.");ret.sort(function(a,b){return a[0] - b[0];});}this.rawData_ = ret;if(annotations.length > 0){this.setAnnotations(annotations,true);}this.attributes_.reparseSeries();}; /** - * Signals to plugins that the chart data has updated. - * This happens after the data has updated but before the chart has redrawn. - */Dygraph.prototype.cascadeDataDidUpdateEvent_ = function(){ // TODO(danvk): there are some issues checking xAxisRange() and using -// toDomCoords from handlers of this event. The visible range should be set -// when the chart is drawn, not derived from the data. -this.cascadeEvents_('dataDidUpdate',{});}; /** - * Get the CSV data. If it's in a function, call that function. If it's in a - * file, do an XMLHttpRequest to get it. - * @private - */Dygraph.prototype.start_ = function(){var data=this.file_; // Functions can return references of all other types. -if(typeof data == 'function'){data = data();}if(utils.isArrayLike(data)){this.rawData_ = this.parseArray_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}else if(typeof data == 'object' && typeof data.getColumnRange == 'function'){ // must be a DataTable from gviz. -this.parseDataTable_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}else if(typeof data == 'string'){ // Heuristic: a newline means it's CSV data. Otherwise it's an URL. -var line_delimiter=utils.detectLineDelimiter(data);if(line_delimiter){this.loadedEvent_(data);}else { // REMOVE_FOR_IE -var req;if(window.XMLHttpRequest){ // Firefox, Opera, IE7, and other browsers will use the native object -req = new XMLHttpRequest();}else { // IE 5 and 6 will use the ActiveX control -req = new ActiveXObject("Microsoft.XMLHTTP");}var caller=this;req.onreadystatechange = function(){if(req.readyState == 4){if(req.status === 200 || // Normal http -req.status === 0){ // Chrome w/ --allow-file-access-from-files -caller.loadedEvent_(req.responseText);}}};req.open("GET",data,true);req.send(null);}}else {console.error("Unknown data format: " + typeof data);}}; /** - * Changes various properties of the graph. These can include: - *
    - *
  • file: changes the source data for the graph
  • - *
  • errorBars: changes whether the data contains stddev
  • - *
- * - * There's a huge variety of options that can be passed to this method. For a - * full list, see http://dygraphs.com/options.html. - * - * @param {Object} input_attrs The new properties and values - * @param {boolean} block_redraw Usually the chart is redrawn after every - * call to updateOptions(). If you know better, you can pass true to - * explicitly block the redraw. This can be useful for chaining - * updateOptions() calls, avoiding the occasional infinite loop and - * preventing redraws when it's not necessary (e.g. when updating a - * callback). - */Dygraph.prototype.updateOptions = function(input_attrs,block_redraw){if(typeof block_redraw == 'undefined')block_redraw = false; // copyUserAttrs_ drops the "file" parameter as a convenience to us. -var file=input_attrs.file;var attrs=Dygraph.copyUserAttrs_(input_attrs); // TODO(danvk): this is a mess. Move these options into attr_. -if('rollPeriod' in attrs){this.rollPeriod_ = attrs.rollPeriod;}if('dateWindow' in attrs){this.dateWindow_ = attrs.dateWindow;if(!('isZoomedIgnoreProgrammaticZoom' in attrs)){this.zoomed_x_ = attrs.dateWindow !== null;}}if('valueRange' in attrs && !('isZoomedIgnoreProgrammaticZoom' in attrs)){this.zoomed_y_ = attrs.valueRange !== null;} // TODO(danvk): validate per-series options. -// Supported: -// strokeWidth -// pointSize -// drawPoints -// highlightCircleSize -// Check if this set options will require new points. -var requiresNewPoints=utils.isPixelChangingOptionList(this.attr_("labels"),attrs);utils.updateDeep(this.user_attrs_,attrs);this.attributes_.reparseSeries();if(file){ // This event indicates that the data is about to change, but hasn't yet. -// TODO(danvk): support cancelation of the update via this event. -this.cascadeEvents_('dataWillUpdate',{});this.file_ = file;if(!block_redraw)this.start_();}else {if(!block_redraw){if(requiresNewPoints){this.predraw_();}else {this.renderGraph_(false);}}}}; /** - * Make a copy of input attributes, removing file as a convenience. - */Dygraph.copyUserAttrs_ = function(attrs){var my_attrs={};for(var k in attrs) {if(!attrs.hasOwnProperty(k))continue;if(k == 'file')continue;if(attrs.hasOwnProperty(k))my_attrs[k] = attrs[k];}return my_attrs;}; /** - * Resizes the dygraph. If no parameters are specified, resizes to fill the - * containing div (which has presumably changed size since the dygraph was - * instantiated. If the width/height are specified, the div will be resized. - * - * This is far more efficient than destroying and re-instantiating a - * Dygraph, since it doesn't have to reparse the underlying data. - * - * @param {number} width Width (in pixels) - * @param {number} height Height (in pixels) - */Dygraph.prototype.resize = function(width,height){if(this.resize_lock){return;}this.resize_lock = true;if(width === null != (height === null)){console.warn("Dygraph.resize() should be called with zero parameters or " + "two non-NULL parameters. Pretending it was zero.");width = height = null;}var old_width=this.width_;var old_height=this.height_;if(width){this.maindiv_.style.width = width + "px";this.maindiv_.style.height = height + "px";this.width_ = width;this.height_ = height;}else {this.width_ = this.maindiv_.clientWidth;this.height_ = this.maindiv_.clientHeight;}if(old_width != this.width_ || old_height != this.height_){ // Resizing a canvas erases it, even when the size doesn't change, so -// any resize needs to be followed by a redraw. -this.resizeElements_();this.predraw_();}this.resize_lock = false;}; /** - * Adjusts the number of points in the rolling average. Updates the graph to - * reflect the new averaging period. - * @param {number} length Number of points over which to average the data. - */Dygraph.prototype.adjustRoll = function(length){this.rollPeriod_ = length;this.predraw_();}; /** - * Returns a boolean array of visibility statuses. - */Dygraph.prototype.visibility = function(){ // Do lazy-initialization, so that this happens after we know the number of -// data series. -if(!this.getOption("visibility")){this.attrs_.visibility = [];} // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs. -while(this.getOption("visibility").length < this.numColumns() - 1) {this.attrs_.visibility.push(true);}return this.getOption("visibility");}; /** - * Changes the visibility of one or more series. - * - * @param {number|number[]|object} num the series index or an array of series indices - * or a boolean array of visibility states by index - * or an object mapping series numbers, as keys, to - * visibility state (boolean values) - * @param {boolean} value the visibility state expressed as a boolean - */Dygraph.prototype.setVisibility = function(num,value){var x=this.visibility();var numIsObject=false;if(!Array.isArray(num)){if(num !== null && typeof num === 'object'){numIsObject = true;}else {num = [num];}}if(numIsObject){for(var i in num) {if(num.hasOwnProperty(i)){if(i < 0 || i >= x.length){console.warn("Invalid series number in setVisibility: " + i);}else {x[i] = num[i];}}}}else {for(var i=0;i < num.length;i++) {if(typeof num[i] === 'boolean'){if(i >= x.length){console.warn("Invalid series number in setVisibility: " + i);}else {x[i] = num[i];}}else {if(num[i] < 0 || num[i] >= x.length){console.warn("Invalid series number in setVisibility: " + num[i]);}else {x[num[i]] = value;}}}}this.predraw_();}; /** - * How large of an area will the dygraph render itself in? - * This is used for testing. - * @return A {width: w, height: h} object. - * @private - */Dygraph.prototype.size = function(){return {width:this.width_,height:this.height_};}; /** - * Update the list of annotations and redraw the chart. - * See dygraphs.com/annotations.html for more info on how to use annotations. - * @param ann {Array} An array of annotation objects. - * @param suppressDraw {Boolean} Set to "true" to block chart redraw (optional). - */Dygraph.prototype.setAnnotations = function(ann,suppressDraw){ // Only add the annotation CSS rule once we know it will be used. -Dygraph.addAnnotationRule();this.annotations_ = ann;if(!this.layout_){console.warn("Tried to setAnnotations before dygraph was ready. " + "Try setting them in a ready() block. See " + "dygraphs.com/tests/annotation.html");return;}this.layout_.setAnnotations(this.annotations_);if(!suppressDraw){this.predraw_();}}; /** - * Return the list of annotations. - */Dygraph.prototype.annotations = function(){return this.annotations_;}; /** - * Get the list of label names for this graph. The first column is the - * x-axis, so the data series names start at index 1. - * - * Returns null when labels have not yet been defined. - */Dygraph.prototype.getLabels = function(){var labels=this.attr_("labels");return labels?labels.slice():null;}; /** - * Get the index of a series (column) given its name. The first column is the - * x-axis, so the data series start with index 1. - */Dygraph.prototype.indexFromSetName = function(name){return this.setIndexByName_[name];}; /** - * Find the row number corresponding to the given x-value. - * Returns null if there is no such x-value in the data. - * If there are multiple rows with the same x-value, this will return the - * first one. - * @param {number} xVal The x-value to look for (e.g. millis since epoch). - * @return {?number} The row number, which you can pass to getValue(), or null. - */Dygraph.prototype.getRowForX = function(xVal){var low=0,high=this.numRows() - 1;while(low <= high) {var idx=high + low >> 1;var x=this.getValue(idx,0);if(x < xVal){low = idx + 1;}else if(x > xVal){high = idx - 1;}else if(low != idx){ // equal, but there may be an earlier match. -high = idx;}else {return idx;}}return null;}; /** - * Trigger a callback when the dygraph has drawn itself and is ready to be - * manipulated. This is primarily useful when dygraphs has to do an XHR for the - * data (i.e. a URL is passed as the data source) and the chart is drawn - * asynchronously. If the chart has already drawn, the callback will fire - * immediately. - * - * This is a good place to call setAnnotation(). - * - * @param {function(!Dygraph)} callback The callback to trigger when the chart - * is ready. - */Dygraph.prototype.ready = function(callback){if(this.is_initial_draw_){this.readyFns_.push(callback);}else {callback.call(this,this);}}; /** - * @private - * Adds a default style for the annotation CSS classes to the document. This is - * only executed when annotations are actually used. It is designed to only be - * called once -- all calls after the first will return immediately. - */Dygraph.addAnnotationRule = function(){ // TODO(danvk): move this function into plugins/annotations.js? -if(Dygraph.addedAnnotationCSS)return;var rule="border: 1px solid black; " + "background-color: white; " + "text-align: center;";var styleSheetElement=document.createElement("style");styleSheetElement.type = "text/css";document.getElementsByTagName("head")[0].appendChild(styleSheetElement); // Find the first style sheet that we can access. -// We may not add a rule to a style sheet from another domain for security -// reasons. This sometimes comes up when using gviz, since the Google gviz JS -// adds its own style sheets from google.com. -for(var i=0;i < document.styleSheets.length;i++) {if(document.styleSheets[i].disabled)continue;var mysheet=document.styleSheets[i];try{if(mysheet.insertRule){ // Firefox -var idx=mysheet.cssRules?mysheet.cssRules.length:0;mysheet.insertRule(".dygraphDefaultAnnotation { " + rule + " }",idx);}else if(mysheet.addRule){ // IE -mysheet.addRule(".dygraphDefaultAnnotation",rule);}Dygraph.addedAnnotationCSS = true;return;}catch(err) { // Was likely a security exception. -}}console.warn("Unable to add default annotation CSS rule; display may be off.");}; /** - * Add an event handler. This event handler is kept until the graph is - * destroyed with a call to graph.destroy(). - * - * @param {!Node} elem The element to add the event to. - * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. - * @param {function(Event):(boolean|undefined)} fn The function to call - * on the event. The function takes one parameter: the event object. - * @private - */Dygraph.prototype.addAndTrackEvent = function(elem,type,fn){utils.addEvent(elem,type,fn);this.registeredEvents_.push({elem:elem,type:type,fn:fn});};Dygraph.prototype.removeTrackedEvents_ = function(){if(this.registeredEvents_){for(var idx=0;idx < this.registeredEvents_.length;idx++) {var reg=this.registeredEvents_[idx];utils.removeEvent(reg.elem,reg.type,reg.fn);}}this.registeredEvents_ = [];}; // Installed plugins, in order of precedence (most-general to most-specific). -Dygraph.PLUGINS = [_pluginsLegend2['default'],_pluginsAxes2['default'],_pluginsRangeSelector2['default'], // Has to be before ChartLabels so that its callbacks are called after ChartLabels' callbacks. -_pluginsChartLabels2['default'],_pluginsAnnotations2['default'],_pluginsGrid2['default']]; // There are many symbols which have historically been available through the -// Dygraph class. These are exported here for backwards compatibility. -Dygraph.GVizChart = _dygraphGviz2['default'];Dygraph.DASHED_LINE = utils.DASHED_LINE;Dygraph.DOT_DASH_LINE = utils.DOT_DASH_LINE;Dygraph.dateAxisLabelFormatter = utils.dateAxisLabelFormatter;Dygraph.toRGB_ = utils.toRGB_;Dygraph.findPos = utils.findPos;Dygraph.pageX = utils.pageX;Dygraph.pageY = utils.pageY;Dygraph.dateString_ = utils.dateString_;Dygraph.defaultInteractionModel = _dygraphInteractionModel2['default'].defaultModel;Dygraph.nonInteractiveModel = Dygraph.nonInteractiveModel_ = _dygraphInteractionModel2['default'].nonInteractiveModel_;Dygraph.Circles = utils.Circles;Dygraph.Plugins = {Legend:_pluginsLegend2['default'],Axes:_pluginsAxes2['default'],Annotations:_pluginsAnnotations2['default'],ChartLabels:_pluginsChartLabels2['default'],Grid:_pluginsGrid2['default'],RangeSelector:_pluginsRangeSelector2['default']};Dygraph.DataHandlers = {DefaultHandler:_datahandlerDefault2['default'],BarsHandler:_datahandlerBars2['default'],CustomBarsHandler:_datahandlerBarsCustom2['default'],DefaultFractionHandler:_datahandlerDefaultFractions2['default'],ErrorBarsHandler:_datahandlerBarsError2['default'],FractionsBarsHandler:_datahandlerBarsFractions2['default']};Dygraph.startPan = _dygraphInteractionModel2['default'].startPan;Dygraph.startZoom = _dygraphInteractionModel2['default'].startZoom;Dygraph.movePan = _dygraphInteractionModel2['default'].movePan;Dygraph.moveZoom = _dygraphInteractionModel2['default'].moveZoom;Dygraph.endPan = _dygraphInteractionModel2['default'].endPan;Dygraph.endZoom = _dygraphInteractionModel2['default'].endZoom;Dygraph.numericLinearTicks = DygraphTickers.numericLinearTicks;Dygraph.numericTicks = DygraphTickers.numericTicks;Dygraph.dateTicker = DygraphTickers.dateTicker;Dygraph.Granularity = DygraphTickers.Granularity;Dygraph.getDateAxis = DygraphTickers.getDateAxis;Dygraph.floatFormat = utils.floatFormat;exports['default'] = Dygraph;module.exports = exports['default']; - -},{"./datahandler/bars":4,"./datahandler/bars-custom":1,"./datahandler/bars-error":2,"./datahandler/bars-fractions":3,"./datahandler/default":7,"./datahandler/default-fractions":6,"./dygraph-canvas":8,"./dygraph-default-attrs":9,"./dygraph-gviz":10,"./dygraph-interaction-model":11,"./dygraph-layout":12,"./dygraph-options":14,"./dygraph-options-reference":13,"./dygraph-tickers":15,"./dygraph-utils":16,"./iframe-tarp":18,"./plugins/annotations":19,"./plugins/axes":20,"./plugins/chart-labels":21,"./plugins/grid":22,"./plugins/legend":23,"./plugins/range-selector":24}],18:[function(require,module,exports){ -/** - * To create a "drag" interaction, you typically register a mousedown event - * handler on the element where the drag begins. In that handler, you register a - * mouseup handler on the window to determine when the mouse is released, - * wherever that release happens. This works well, except when the user releases - * the mouse over an off-domain iframe. In that case, the mouseup event is - * handled by the iframe and never bubbles up to the window handler. - * - * To deal with this issue, we cover iframes with high z-index divs to make sure - * they don't capture mouseup. - * - * Usage: - * element.addEventListener('mousedown', function() { - * var tarper = new IFrameTarp(); - * tarper.cover(); - * var mouseUpHandler = function() { - * ... - * window.removeEventListener(mouseUpHandler); - * tarper.uncover(); - * }; - * window.addEventListener('mouseup', mouseUpHandler); - * }; - * - * @constructor - */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -function IFrameTarp() { - /** @type {Array.} */ - this.tarps = []; -}; - -/** - * Find all the iframes in the document and cover them with high z-index - * transparent divs. - */ -IFrameTarp.prototype.cover = function () { - var iframes = document.getElementsByTagName("iframe"); - for (var i = 0; i < iframes.length; i++) { - var iframe = iframes[i]; - var pos = utils.findPos(iframe), - x = pos.x, - y = pos.y, - width = iframe.offsetWidth, - height = iframe.offsetHeight; - - var div = document.createElement("div"); - div.style.position = "absolute"; - div.style.left = x + 'px'; - div.style.top = y + 'px'; - div.style.width = width + 'px'; - div.style.height = height + 'px'; - div.style.zIndex = 999; - document.body.appendChild(div); - this.tarps.push(div); - } -}; - -/** - * Remove all the iframe covers. You should call this in a mouseup handler. - */ -IFrameTarp.prototype.uncover = function () { - for (var i = 0; i < this.tarps.length; i++) { - this.tarps[i].parentNode.removeChild(this.tarps[i]); - } - this.tarps = []; -}; - -exports["default"] = IFrameTarp; -module.exports = exports["default"]; - -},{}],19:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/*global Dygraph:false */ - -"use strict"; - -/** -Current bits of jankiness: -- Uses dygraph.layout_ to get the parsed annotations. -- Uses dygraph.plotter_.area - -It would be nice if the plugin didn't require so much special support inside -the core dygraphs classes, but annotations involve quite a bit of parsing and -layout. - -TODO(danvk): cache DOM elements. -*/ - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var annotations = function annotations() { - this.annotations_ = []; -}; - -annotations.prototype.toString = function () { - return "Annotations Plugin"; -}; - -annotations.prototype.activate = function (g) { - return { - clearChart: this.clearChart, - didDrawChart: this.didDrawChart - }; -}; - -annotations.prototype.detachLabels = function () { - for (var i = 0; i < this.annotations_.length; i++) { - var a = this.annotations_[i]; - if (a.parentNode) a.parentNode.removeChild(a); - this.annotations_[i] = null; - } - this.annotations_ = []; -}; - -annotations.prototype.clearChart = function (e) { - this.detachLabels(); -}; - -annotations.prototype.didDrawChart = function (e) { - var g = e.dygraph; - - // Early out in the (common) case of zero annotations. - var points = g.layout_.annotated_points; - if (!points || points.length === 0) return; - - var containerDiv = e.canvas.parentNode; - var annotationStyle = { - "position": "absolute", - "fontSize": g.getOption('axisLabelFontSize') + "px", - "zIndex": 10, - "overflow": "hidden" - }; - - var bindEvt = function bindEvt(eventName, classEventName, pt) { - return function (annotation_event) { - var a = pt.annotation; - if (a.hasOwnProperty(eventName)) { - a[eventName](a, pt, g, annotation_event); - } else if (g.getOption(classEventName)) { - g.getOption(classEventName)(a, pt, g, annotation_event); - } - }; - }; - - // Add the annotations one-by-one. - var area = e.dygraph.plotter_.area; - - // x-coord to sum of previous annotation's heights (used for stacking). - var xToUsedHeight = {}; - - for (var i = 0; i < points.length; i++) { - var p = points[i]; - if (p.canvasx < area.x || p.canvasx > area.x + area.w || p.canvasy < area.y || p.canvasy > area.y + area.h) { - continue; - } - - var a = p.annotation; - var tick_height = 6; - if (a.hasOwnProperty("tickHeight")) { - tick_height = a.tickHeight; - } - - var div = document.createElement("div"); - for (var name in annotationStyle) { - if (annotationStyle.hasOwnProperty(name)) { - div.style[name] = annotationStyle[name]; - } - } - if (!a.hasOwnProperty('icon')) { - div.className = "dygraphDefaultAnnotation"; - } - if (a.hasOwnProperty('cssClass')) { - div.className += " " + a.cssClass; - } - - var width = a.hasOwnProperty('width') ? a.width : 16; - var height = a.hasOwnProperty('height') ? a.height : 16; - if (a.hasOwnProperty('icon')) { - var img = document.createElement("img"); - img.src = a.icon; - img.width = width; - img.height = height; - div.appendChild(img); - } else if (p.annotation.hasOwnProperty('shortText')) { - div.appendChild(document.createTextNode(p.annotation.shortText)); - } - var left = p.canvasx - width / 2; - div.style.left = left + "px"; - var divTop = 0; - if (a.attachAtBottom) { - var y = area.y + area.h - height - tick_height; - if (xToUsedHeight[left]) { - y -= xToUsedHeight[left]; - } else { - xToUsedHeight[left] = 0; - } - xToUsedHeight[left] += tick_height + height; - divTop = y; - } else { - divTop = p.canvasy - height - tick_height; - } - div.style.top = divTop + "px"; - div.style.width = width + "px"; - div.style.height = height + "px"; - div.title = p.annotation.text; - div.style.color = g.colorsMap_[p.name]; - div.style.borderColor = g.colorsMap_[p.name]; - a.div = div; - - g.addAndTrackEvent(div, 'click', bindEvt('clickHandler', 'annotationClickHandler', p, this)); - g.addAndTrackEvent(div, 'mouseover', bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this)); - g.addAndTrackEvent(div, 'mouseout', bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this)); - g.addAndTrackEvent(div, 'dblclick', bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this)); - - containerDiv.appendChild(div); - this.annotations_.push(div); - - var ctx = e.drawingContext; - ctx.save(); - ctx.strokeStyle = g.colorsMap_[p.name]; - ctx.beginPath(); - if (!a.attachAtBottom) { - ctx.moveTo(p.canvasx, p.canvasy); - ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height); - } else { - var y = divTop + height; - ctx.moveTo(p.canvasx, y); - ctx.lineTo(p.canvasx, y + tick_height); - } - ctx.closePath(); - ctx.stroke(); - ctx.restore(); - } -}; - -annotations.prototype.destroy = function () { - this.detachLabels(); -}; - -exports["default"] = annotations; -module.exports = exports["default"]; - -},{}],20:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ - -/*global Dygraph:false */ - -'use strict'; - -/* -Bits of jankiness: -- Direct layout access -- Direct area access -- Should include calculation of ticks, not just the drawing. - -Options left to make axis-friendly. - ('drawAxesAtZero') - ('xAxisHeight') -*/ - -/** - * Draws the axes. This includes the labels on the x- and y-axes, as well - * as the tick marks on the axes. - * It does _not_ draw the grid lines which span the entire chart. - */ -Object.defineProperty(exports, '__esModule', { - value: true -}); -var axes = function axes() { - this.xlabels_ = []; - this.ylabels_ = []; -}; - -axes.prototype.toString = function () { - return 'Axes Plugin'; -}; - -axes.prototype.activate = function (g) { - return { - layout: this.layout, - clearChart: this.clearChart, - willDrawChart: this.willDrawChart - }; -}; - -axes.prototype.layout = function (e) { - var g = e.dygraph; - - if (g.getOptionForAxis('drawAxis', 'y')) { - var w = g.getOptionForAxis('axisLabelWidth', 'y') + 2 * g.getOptionForAxis('axisTickSize', 'y'); - e.reserveSpaceLeft(w); - } - - if (g.getOptionForAxis('drawAxis', 'x')) { - var h; - // NOTE: I think this is probably broken now, since g.getOption() now - // hits the dictionary. (That is, g.getOption('xAxisHeight') now always - // has a value.) - if (g.getOption('xAxisHeight')) { - h = g.getOption('xAxisHeight'); - } else { - h = g.getOptionForAxis('axisLabelFontSize', 'x') + 2 * g.getOptionForAxis('axisTickSize', 'x'); - } - e.reserveSpaceBottom(h); - } - - if (g.numAxes() == 2) { - if (g.getOptionForAxis('drawAxis', 'y2')) { - var w = g.getOptionForAxis('axisLabelWidth', 'y2') + 2 * g.getOptionForAxis('axisTickSize', 'y2'); - e.reserveSpaceRight(w); - } - } else if (g.numAxes() > 2) { - g.error('Only two y-axes are supported at this time. (Trying ' + 'to use ' + g.numAxes() + ')'); - } -}; - -axes.prototype.detachLabels = function () { - function removeArray(ary) { - for (var i = 0; i < ary.length; i++) { - var el = ary[i]; - if (el.parentNode) el.parentNode.removeChild(el); - } - } - - removeArray(this.xlabels_); - removeArray(this.ylabels_); - this.xlabels_ = []; - this.ylabels_ = []; -}; - -axes.prototype.clearChart = function (e) { - this.detachLabels(); -}; - -axes.prototype.willDrawChart = function (e) { - var g = e.dygraph; - - if (!g.getOptionForAxis('drawAxis', 'x') && !g.getOptionForAxis('drawAxis', 'y') && !g.getOptionForAxis('drawAxis', 'y2')) { - return; - } - - // Round pixels to half-integer boundaries for crisper drawing. - function halfUp(x) { - return Math.round(x) + 0.5; - } - function halfDown(y) { - return Math.round(y) - 0.5; - } - - var context = e.drawingContext; - var containerDiv = e.canvas.parentNode; - var canvasWidth = g.width_; // e.canvas.width is affected by pixel ratio. - var canvasHeight = g.height_; - - var label, x, y, tick, i; - - var makeLabelStyle = function makeLabelStyle(axis) { - return { - position: 'absolute', - fontSize: g.getOptionForAxis('axisLabelFontSize', axis) + 'px', - zIndex: 10, - color: g.getOptionForAxis('axisLabelColor', axis), - width: g.getOptionForAxis('axisLabelWidth', axis) + 'px', - // height: g.getOptionForAxis('axisLabelFontSize', 'x') + 2 + "px", - lineHeight: 'normal', // Something other than "normal" line-height screws up label positioning. - overflow: 'hidden' - }; - }; - - var labelStyles = { - x: makeLabelStyle('x'), - y: makeLabelStyle('y'), - y2: makeLabelStyle('y2') - }; - - var makeDiv = function makeDiv(txt, axis, prec_axis) { - /* - * This seems to be called with the following three sets of axis/prec_axis: - * x: undefined - * y: y1 - * y: y2 - */ - var div = document.createElement('div'); - var labelStyle = labelStyles[prec_axis == 'y2' ? 'y2' : axis]; - for (var name in labelStyle) { - if (labelStyle.hasOwnProperty(name)) { - div.style[name] = labelStyle[name]; - } - } - var inner_div = document.createElement('div'); - inner_div.className = 'dygraph-axis-label' + ' dygraph-axis-label-' + axis + (prec_axis ? ' dygraph-axis-label-' + prec_axis : ''); - inner_div.innerHTML = txt; - div.appendChild(inner_div); - return div; - }; - - // axis lines - context.save(); - - var layout = g.layout_; - var area = e.dygraph.plotter_.area; - - // Helper for repeated axis-option accesses. - var makeOptionGetter = function makeOptionGetter(axis) { - return function (option) { - return g.getOptionForAxis(option, axis); - }; - }; - - if (g.getOptionForAxis('drawAxis', 'y')) { - if (layout.yticks && layout.yticks.length > 0) { - var num_axes = g.numAxes(); - var getOptions = [makeOptionGetter('y'), makeOptionGetter('y2')]; - for (i = 0; i < layout.yticks.length; i++) { - tick = layout.yticks[i]; - if (typeof tick == 'function') return; // <-- when would this happen? - x = area.x; - var sgn = 1; - var prec_axis = 'y1'; - var getAxisOption = getOptions[0]; - if (tick[0] == 1) { - // right-side y-axis - x = area.x + area.w; - sgn = -1; - prec_axis = 'y2'; - getAxisOption = getOptions[1]; - } - var fontSize = getAxisOption('axisLabelFontSize'); - y = area.y + tick[1] * area.h; - - /* Tick marks are currently clipped, so don't bother drawing them. - context.beginPath(); - context.moveTo(halfUp(x), halfDown(y)); - context.lineTo(halfUp(x - sgn * this.attr_('axisTickSize')), halfDown(y)); - context.closePath(); - context.stroke(); - */ - - label = makeDiv(tick[2], 'y', num_axes == 2 ? prec_axis : null); - var top = y - fontSize / 2; - if (top < 0) top = 0; - - if (top + fontSize + 3 > canvasHeight) { - label.style.bottom = '0'; - } else { - label.style.top = top + 'px'; - } - if (tick[0] === 0) { - label.style.left = area.x - getAxisOption('axisLabelWidth') - getAxisOption('axisTickSize') + 'px'; - label.style.textAlign = 'right'; - } else if (tick[0] == 1) { - label.style.left = area.x + area.w + getAxisOption('axisTickSize') + 'px'; - label.style.textAlign = 'left'; - } - label.style.width = getAxisOption('axisLabelWidth') + 'px'; - containerDiv.appendChild(label); - this.ylabels_.push(label); - } - - // The lowest tick on the y-axis often overlaps with the leftmost - // tick on the x-axis. Shift the bottom tick up a little bit to - // compensate if necessary. - var bottomTick = this.ylabels_[0]; - // Interested in the y2 axis also? - var fontSize = g.getOptionForAxis('axisLabelFontSize', 'y'); - var bottom = parseInt(bottomTick.style.top, 10) + fontSize; - if (bottom > canvasHeight - fontSize) { - bottomTick.style.top = parseInt(bottomTick.style.top, 10) - fontSize / 2 + 'px'; - } - } - - // draw a vertical line on the left to separate the chart from the labels. - var axisX; - if (g.getOption('drawAxesAtZero')) { - var r = g.toPercentXCoord(0); - if (r > 1 || r < 0 || isNaN(r)) r = 0; - axisX = halfUp(area.x + r * area.w); - } else { - axisX = halfUp(area.x); - } - - context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y'); - context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y'); - - context.beginPath(); - context.moveTo(axisX, halfDown(area.y)); - context.lineTo(axisX, halfDown(area.y + area.h)); - context.closePath(); - context.stroke(); - - // if there's a secondary y-axis, draw a vertical line for that, too. - if (g.numAxes() == 2) { - context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y2'); - context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y2'); - context.beginPath(); - context.moveTo(halfDown(area.x + area.w), halfDown(area.y)); - context.lineTo(halfDown(area.x + area.w), halfDown(area.y + area.h)); - context.closePath(); - context.stroke(); - } - } - - if (g.getOptionForAxis('drawAxis', 'x')) { - if (layout.xticks) { - var getAxisOption = makeOptionGetter('x'); - for (i = 0; i < layout.xticks.length; i++) { - tick = layout.xticks[i]; - x = area.x + tick[0] * area.w; - y = area.y + area.h; - - /* Tick marks are currently clipped, so don't bother drawing them. - context.beginPath(); - context.moveTo(halfUp(x), halfDown(y)); - context.lineTo(halfUp(x), halfDown(y + this.attr_('axisTickSize'))); - context.closePath(); - context.stroke(); - */ - - label = makeDiv(tick[1], 'x'); - label.style.textAlign = 'center'; - label.style.top = y + getAxisOption('axisTickSize') + 'px'; - - var left = x - getAxisOption('axisLabelWidth') / 2; - if (left + getAxisOption('axisLabelWidth') > canvasWidth) { - left = canvasWidth - getAxisOption('axisLabelWidth'); - label.style.textAlign = 'right'; - } - if (left < 0) { - left = 0; - label.style.textAlign = 'left'; - } - - label.style.left = left + 'px'; - label.style.width = getAxisOption('axisLabelWidth') + 'px'; - containerDiv.appendChild(label); - this.xlabels_.push(label); - } - } - - context.strokeStyle = g.getOptionForAxis('axisLineColor', 'x'); - context.lineWidth = g.getOptionForAxis('axisLineWidth', 'x'); - context.beginPath(); - var axisY; - if (g.getOption('drawAxesAtZero')) { - var r = g.toPercentYCoord(0, 0); - if (r > 1 || r < 0) r = 1; - axisY = halfDown(area.y + r * area.h); - } else { - axisY = halfDown(area.y + area.h); - } - context.moveTo(halfUp(area.x), axisY); - context.lineTo(halfUp(area.x + area.w), axisY); - context.closePath(); - context.stroke(); - } - - context.restore(); -}; - -exports['default'] = axes; -module.exports = exports['default']; - -},{}],21:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ -/*global Dygraph:false */ - -"use strict"; - -// TODO(danvk): move chart label options out of dygraphs and into the plugin. -// TODO(danvk): only tear down & rebuild the DIVs when it's necessary. - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var chart_labels = function chart_labels() { - this.title_div_ = null; - this.xlabel_div_ = null; - this.ylabel_div_ = null; - this.y2label_div_ = null; -}; - -chart_labels.prototype.toString = function () { - return "ChartLabels Plugin"; -}; - -chart_labels.prototype.activate = function (g) { - return { - layout: this.layout, - // clearChart: this.clearChart, - didDrawChart: this.didDrawChart - }; -}; - -// QUESTION: should there be a plugin-utils.js? -var createDivInRect = function createDivInRect(r) { - var div = document.createElement('div'); - div.style.position = 'absolute'; - div.style.left = r.x + 'px'; - div.style.top = r.y + 'px'; - div.style.width = r.w + 'px'; - div.style.height = r.h + 'px'; - return div; -}; - -// Detach and null out any existing nodes. -chart_labels.prototype.detachLabels_ = function () { - var els = [this.title_div_, this.xlabel_div_, this.ylabel_div_, this.y2label_div_]; - for (var i = 0; i < els.length; i++) { - var el = els[i]; - if (!el) continue; - if (el.parentNode) el.parentNode.removeChild(el); - } - - this.title_div_ = null; - this.xlabel_div_ = null; - this.ylabel_div_ = null; - this.y2label_div_ = null; -}; - -var createRotatedDiv = function createRotatedDiv(g, box, axis, classes, html) { - // TODO(danvk): is this outer div actually necessary? - var div = document.createElement("div"); - div.style.position = 'absolute'; - if (axis == 1) { - // NOTE: this is cheating. Should be positioned relative to the box. - div.style.left = '0px'; - } else { - div.style.left = box.x + 'px'; - } - div.style.top = box.y + 'px'; - div.style.width = box.w + 'px'; - div.style.height = box.h + 'px'; - div.style.fontSize = g.getOption('yLabelWidth') - 2 + 'px'; - - var inner_div = document.createElement("div"); - inner_div.style.position = 'absolute'; - inner_div.style.width = box.h + 'px'; - inner_div.style.height = box.w + 'px'; - inner_div.style.top = box.h / 2 - box.w / 2 + 'px'; - inner_div.style.left = box.w / 2 - box.h / 2 + 'px'; - inner_div.style.textAlign = 'center'; - - // CSS rotation is an HTML5 feature which is not standardized. Hence every - // browser has its own name for the CSS style. - var val = 'rotate(' + (axis == 1 ? '-' : '') + '90deg)'; - inner_div.style.transform = val; // HTML5 - inner_div.style.WebkitTransform = val; // Safari/Chrome - inner_div.style.MozTransform = val; // Firefox - inner_div.style.OTransform = val; // Opera - inner_div.style.msTransform = val; // IE9 - - var class_div = document.createElement("div"); - class_div.className = classes; - class_div.innerHTML = html; - - inner_div.appendChild(class_div); - div.appendChild(inner_div); - return div; -}; - -chart_labels.prototype.layout = function (e) { - this.detachLabels_(); - - var g = e.dygraph; - var div = e.chart_div; - if (g.getOption('title')) { - // QUESTION: should this return an absolutely-positioned div instead? - var title_rect = e.reserveSpaceTop(g.getOption('titleHeight')); - this.title_div_ = createDivInRect(title_rect); - this.title_div_.style.textAlign = 'center'; - this.title_div_.style.fontSize = g.getOption('titleHeight') - 8 + 'px'; - this.title_div_.style.fontWeight = 'bold'; - this.title_div_.style.zIndex = 10; - - var class_div = document.createElement("div"); - class_div.className = 'dygraph-label dygraph-title'; - class_div.innerHTML = g.getOption('title'); - this.title_div_.appendChild(class_div); - div.appendChild(this.title_div_); - } - - if (g.getOption('xlabel')) { - var x_rect = e.reserveSpaceBottom(g.getOption('xLabelHeight')); - this.xlabel_div_ = createDivInRect(x_rect); - this.xlabel_div_.style.textAlign = 'center'; - this.xlabel_div_.style.fontSize = g.getOption('xLabelHeight') - 2 + 'px'; - - var class_div = document.createElement("div"); - class_div.className = 'dygraph-label dygraph-xlabel'; - class_div.innerHTML = g.getOption('xlabel'); - this.xlabel_div_.appendChild(class_div); - div.appendChild(this.xlabel_div_); - } - - if (g.getOption('ylabel')) { - // It would make sense to shift the chart here to make room for the y-axis - // label, but the default yAxisLabelWidth is large enough that this results - // in overly-padded charts. The y-axis label should fit fine. If it - // doesn't, the yAxisLabelWidth option can be increased. - var y_rect = e.reserveSpaceLeft(0); - - this.ylabel_div_ = createRotatedDiv(g, y_rect, 1, // primary (left) y-axis - 'dygraph-label dygraph-ylabel', g.getOption('ylabel')); - div.appendChild(this.ylabel_div_); - } - - if (g.getOption('y2label') && g.numAxes() == 2) { - // same logic applies here as for ylabel. - var y2_rect = e.reserveSpaceRight(0); - this.y2label_div_ = createRotatedDiv(g, y2_rect, 2, // secondary (right) y-axis - 'dygraph-label dygraph-y2label', g.getOption('y2label')); - div.appendChild(this.y2label_div_); - } -}; - -chart_labels.prototype.didDrawChart = function (e) { - var g = e.dygraph; - if (this.title_div_) { - this.title_div_.children[0].innerHTML = g.getOption('title'); - } - if (this.xlabel_div_) { - this.xlabel_div_.children[0].innerHTML = g.getOption('xlabel'); - } - if (this.ylabel_div_) { - this.ylabel_div_.children[0].children[0].innerHTML = g.getOption('ylabel'); - } - if (this.y2label_div_) { - this.y2label_div_.children[0].children[0].innerHTML = g.getOption('y2label'); - } -}; - -chart_labels.prototype.clearChart = function () {}; - -chart_labels.prototype.destroy = function () { - this.detachLabels_(); -}; - -exports["default"] = chart_labels; -module.exports = exports["default"]; - -},{}],22:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ -/*global Dygraph:false */ - -/* - -Current bits of jankiness: -- Direct layout access -- Direct area access - -*/ - -"use strict"; - -/** - * Draws the gridlines, i.e. the gray horizontal & vertical lines running the - * length of the chart. - * - * @constructor - */ -Object.defineProperty(exports, "__esModule", { - value: true -}); -var grid = function grid() {}; - -grid.prototype.toString = function () { - return "Gridline Plugin"; -}; - -grid.prototype.activate = function (g) { - return { - willDrawChart: this.willDrawChart - }; -}; - -grid.prototype.willDrawChart = function (e) { - // Draw the new X/Y grid. Lines appear crisper when pixels are rounded to - // half-integers. This prevents them from drawing in two rows/cols. - var g = e.dygraph; - var ctx = e.drawingContext; - var layout = g.layout_; - var area = e.dygraph.plotter_.area; - - function halfUp(x) { - return Math.round(x) + 0.5; - } - function halfDown(y) { - return Math.round(y) - 0.5; - } - - var x, y, i, ticks; - if (g.getOptionForAxis('drawGrid', 'y')) { - var axes = ["y", "y2"]; - var strokeStyles = [], - lineWidths = [], - drawGrid = [], - stroking = [], - strokePattern = []; - for (var i = 0; i < axes.length; i++) { - drawGrid[i] = g.getOptionForAxis('drawGrid', axes[i]); - if (drawGrid[i]) { - strokeStyles[i] = g.getOptionForAxis('gridLineColor', axes[i]); - lineWidths[i] = g.getOptionForAxis('gridLineWidth', axes[i]); - strokePattern[i] = g.getOptionForAxis('gridLinePattern', axes[i]); - stroking[i] = strokePattern[i] && strokePattern[i].length >= 2; - } - } - ticks = layout.yticks; - ctx.save(); - // draw grids for the different y axes - for (i = 0; i < ticks.length; i++) { - var axis = ticks[i][0]; - if (drawGrid[axis]) { - ctx.save(); - if (stroking[axis]) { - if (ctx.setLineDash) ctx.setLineDash(strokePattern[axis]); - } - ctx.strokeStyle = strokeStyles[axis]; - ctx.lineWidth = lineWidths[axis]; - - x = halfUp(area.x); - y = halfDown(area.y + ticks[i][1] * area.h); - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(x + area.w, y); - ctx.stroke(); - - ctx.restore(); - } - } - ctx.restore(); - } - - // draw grid for x axis - if (g.getOptionForAxis('drawGrid', 'x')) { - ticks = layout.xticks; - ctx.save(); - var strokePattern = g.getOptionForAxis('gridLinePattern', 'x'); - var stroking = strokePattern && strokePattern.length >= 2; - if (stroking) { - if (ctx.setLineDash) ctx.setLineDash(strokePattern); - } - ctx.strokeStyle = g.getOptionForAxis('gridLineColor', 'x'); - ctx.lineWidth = g.getOptionForAxis('gridLineWidth', 'x'); - for (i = 0; i < ticks.length; i++) { - x = halfUp(area.x + ticks[i][0] * area.w); - y = halfDown(area.y + area.h); - ctx.beginPath(); - ctx.moveTo(x, y); - ctx.lineTo(x, area.y); - ctx.closePath(); - ctx.stroke(); - } - if (stroking) { - if (ctx.setLineDash) ctx.setLineDash([]); - } - ctx.restore(); - } -}; - -grid.prototype.destroy = function () {}; - -exports["default"] = grid; -module.exports = exports["default"]; - -},{}],23:[function(require,module,exports){ -/** - * @license - * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ -/*global Dygraph:false */ - -/* -Current bits of jankiness: -- Uses two private APIs: - 1. Dygraph.optionsViewForAxis_ - 2. dygraph.plotter_.area -- Registers for a "predraw" event, which should be renamed. -- I call calculateEmWidthInDiv more often than needed. -*/ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } - -var _dygraphUtils = require('../dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -/** - * Creates the legend, which appears when the user hovers over the chart. - * The legend can be either a user-specified or generated div. - * - * @constructor - */ -var Legend = function Legend() { - this.legend_div_ = null; - this.is_generated_div_ = false; // do we own this div, or was it user-specified? -}; - -Legend.prototype.toString = function () { - return "Legend Plugin"; -}; - -// (defined below) -var generateLegendDashHTML; - -/** - * This is called during the dygraph constructor, after options have been set - * but before the data is available. - * - * Proper tasks to do here include: - * - Reading your own options - * - DOM manipulation - * - Registering event listeners - * - * @param {Dygraph} g Graph instance. - * @return {object.} Mapping of event names to callbacks. - */ -Legend.prototype.activate = function (g) { - var div; - var divWidth = g.getOption('labelsDivWidth'); - - var userLabelsDiv = g.getOption('labelsDiv'); - if (userLabelsDiv && null !== userLabelsDiv) { - if (typeof userLabelsDiv == "string" || userLabelsDiv instanceof String) { - div = document.getElementById(userLabelsDiv); - } else { - div = userLabelsDiv; - } - } else { - // Default legend styles. These can be overridden in CSS by adding - // "!important" after your rule, e.g. "left: 30px !important;" - var messagestyle = { - "position": "absolute", - "fontSize": "14px", - "zIndex": 10, - "width": divWidth + "px", - "top": "0px", - "left": g.size().width - divWidth - 2 + "px", - "background": "white", - "lineHeight": "normal", - "textAlign": "left", - "overflow": "hidden" }; - - // TODO(danvk): get rid of labelsDivStyles? CSS is better. - utils.update(messagestyle, g.getOption('labelsDivStyles')); - div = document.createElement("div"); - div.className = "dygraph-legend"; - for (var name in messagestyle) { - if (!messagestyle.hasOwnProperty(name)) continue; - - try { - div.style[name] = messagestyle[name]; - } catch (e) { - console.warn("You are using unsupported css properties for your " + "browser in labelsDivStyles"); - } - } - - // TODO(danvk): come up with a cleaner way to expose this. - g.graphDiv.appendChild(div); - this.is_generated_div_ = true; - } - - this.legend_div_ = div; - this.one_em_width_ = 10; // just a guess, will be updated. - - return { - select: this.select, - deselect: this.deselect, - // TODO(danvk): rethink the name "predraw" before we commit to it in any API. - predraw: this.predraw, - didDrawChart: this.didDrawChart - }; -}; - -// Needed for dashed lines. -var calculateEmWidthInDiv = function calculateEmWidthInDiv(div) { - var sizeSpan = document.createElement('span'); - sizeSpan.setAttribute('style', 'margin: 0; padding: 0 0 0 1em; border: 0;'); - div.appendChild(sizeSpan); - var oneEmWidth = sizeSpan.offsetWidth; - div.removeChild(sizeSpan); - return oneEmWidth; -}; - -var escapeHTML = function escapeHTML(str) { - return str.replace(/&/g, "&").replace(/"/g, """).replace(//g, ">"); -}; - -Legend.prototype.select = function (e) { - var xValue = e.selectedX; - var points = e.selectedPoints; - var row = e.selectedRow; - - var legendMode = e.dygraph.getOption('legend'); - if (legendMode === 'never') { - this.legend_div_.style.display = 'none'; - return; - } - - if (legendMode === 'follow') { - // create floating legend div - var area = e.dygraph.plotter_.area; - var labelsDivWidth = e.dygraph.getOption('labelsDivWidth'); - var yAxisLabelWidth = e.dygraph.getOptionForAxis('axisLabelWidth', 'y'); - // determine floating [left, top] coordinates of the legend div - // within the plotter_ area - // offset 50 px to the right and down from the first selection point - // 50 px is guess based on mouse cursor size - var leftLegend = points[0].x * area.w + 50; - var topLegend = points[0].y * area.h - 50; - - // if legend floats to end of the chart area, it flips to the other - // side of the selection point - if (leftLegend + labelsDivWidth + 1 > area.w) { - leftLegend = leftLegend - 2 * 50 - labelsDivWidth - (yAxisLabelWidth - area.x); - } - - e.dygraph.graphDiv.appendChild(this.legend_div_); - this.legend_div_.style.left = yAxisLabelWidth + leftLegend + "px"; - this.legend_div_.style.top = topLegend + "px"; - } - - var html = Legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_, row); - this.legend_div_.innerHTML = html; - this.legend_div_.style.display = ''; -}; - -Legend.prototype.deselect = function (e) { - var legendMode = e.dygraph.getOption('legend'); - if (legendMode !== 'always') { - this.legend_div_.style.display = "none"; - } - - // Have to do this every time, since styles might have changed. - var oneEmWidth = calculateEmWidthInDiv(this.legend_div_); - this.one_em_width_ = oneEmWidth; - - var html = Legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth, null); - this.legend_div_.innerHTML = html; -}; - -Legend.prototype.didDrawChart = function (e) { - this.deselect(e); -}; - -// Right edge should be flush with the right edge of the charting area (which -// may not be the same as the right edge of the div, if we have two y-axes. -// TODO(danvk): is any of this really necessary? Could just set "right" in "activate". -/** - * Position the labels div so that: - * - its right edge is flush with the right edge of the charting area - * - its top edge is flush with the top edge of the charting area - * @private - */ -Legend.prototype.predraw = function (e) { - // Don't touch a user-specified labelsDiv. - if (!this.is_generated_div_) return; - - // TODO(danvk): only use real APIs for this. - e.dygraph.graphDiv.appendChild(this.legend_div_); - var area = e.dygraph.getArea(); - var labelsDivWidth = e.dygraph.getOption("labelsDivWidth"); - this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px"; - this.legend_div_.style.top = area.y + "px"; - this.legend_div_.style.width = labelsDivWidth + "px"; -}; - -/** - * Called when dygraph.destroy() is called. - * You should null out any references and detach any DOM elements. - */ -Legend.prototype.destroy = function () { - this.legend_div_ = null; -}; - -/** - * Generates HTML for the legend which is displayed when hovering over the - * chart. If no selected points are specified, a default legend is returned - * (this may just be the empty string). - * @param {number} x The x-value of the selected points. - * @param {Object} sel_points List of selected points for the given - * x-value. Should have properties like 'name', 'yval' and 'canvasy'. - * @param {number} oneEmWidth The pixel width for 1em in the legend. Only - * relevant when displaying a legend with no selection (i.e. {legend: - * 'always'}) and with dashed lines. - * @param {number} row The selected row index. - * @private - */ -Legend.generateLegendHTML = function (g, x, sel_points, oneEmWidth, row) { - // Data about the selection to pass to legendFormatter - var data = { - dygraph: g, - x: x, - series: [] - }; - - var labelToSeries = {}; - var labels = g.getLabels(); - if (labels) { - for (var i = 1; i < labels.length; i++) { - var series = g.getPropertiesForSeries(labels[i]); - var strokePattern = g.getOption('strokePattern', labels[i]); - var seriesData = { - dashHTML: generateLegendDashHTML(strokePattern, series.color, oneEmWidth), - label: labels[i], - labelHTML: escapeHTML(labels[i]), - isVisible: series.visible, - color: series.color - }; - - data.series.push(seriesData); - labelToSeries[labels[i]] = seriesData; - } - } - - if (typeof x !== 'undefined') { - var xOptView = g.optionsViewForAxis_('x'); - var xvf = xOptView('valueFormatter'); - data.xHTML = xvf.call(g, x, xOptView, labels[0], g, row, 0); - - var yOptViews = []; - var num_axes = g.numAxes(); - for (var i = 0; i < num_axes; i++) { - // TODO(danvk): remove this use of a private API - yOptViews[i] = g.optionsViewForAxis_('y' + (i ? 1 + i : '')); - } - - var showZeros = g.getOption('labelsShowZeroValues'); - var highlightSeries = g.getHighlightSeries(); - for (i = 0; i < sel_points.length; i++) { - var pt = sel_points[i]; - var seriesData = labelToSeries[pt.name]; - seriesData.y = pt.yval; - - if (pt.yval === 0 && !showZeros || isNaN(pt.canvasy)) { - seriesData.isVisible = false; - continue; - } - - var series = g.getPropertiesForSeries(pt.name); - var yOptView = yOptViews[series.axis - 1]; - var fmtFunc = yOptView('valueFormatter'); - var yHTML = fmtFunc.call(g, pt.yval, yOptView, pt.name, g, row, labels.indexOf(pt.name)); - - utils.update(seriesData, { yHTML: yHTML }); - - if (pt.name == highlightSeries) { - seriesData.isHighlighted = true; - } - } - } - - var formatter = g.getOption('legendFormatter') || Legend.defaultFormatter; - return formatter.call(g, data); -}; - -Legend.defaultFormatter = function (data) { - var g = data.dygraph; - - // TODO(danvk): deprecate this option in place of {legend: 'never'} - // XXX should this logic be in the formatter? - if (g.getOption('showLabelsOnHighlight') !== true) return ''; - - var sepLines = g.getOption('labelsSeparateLines'); - var html; - - if (typeof data.x === 'undefined') { - // TODO: this check is duplicated in generateLegendHTML. Put it in one place. - if (g.getOption('legend') != 'always') { - return ''; - } - - html = ''; - for (var i = 0; i < data.series.length; i++) { - var series = data.series[i]; - if (!series.isVisible) continue; - - if (html !== '') html += sepLines ? '
' : ' '; - html += "" + series.dashHTML + " " + series.labelHTML + ""; - } - return html; - } - - html = data.xHTML + ':'; - for (var i = 0; i < data.series.length; i++) { - var series = data.series[i]; - if (!series.isVisible) continue; - if (sepLines) html += '
'; - var cls = series.isHighlighted ? ' class="highlight"' : ''; - html += " " + series.labelHTML + ": " + series.yHTML + ""; - } - return html; -}; - -/** - * Generates html for the "dash" displayed on the legend when using "legend: always". - * In particular, this works for dashed lines with any stroke pattern. It will - * try to scale the pattern to fit in 1em width. Or if small enough repeat the - * pattern for 1em width. - * - * @param strokePattern The pattern - * @param color The color of the series. - * @param oneEmWidth The width in pixels of 1em in the legend. - * @private - */ -// TODO(danvk): cache the results of this -generateLegendDashHTML = function (strokePattern, color, oneEmWidth) { - // Easy, common case: a solid line - if (!strokePattern || strokePattern.length <= 1) { - return "
"; - } - - var i, j, paddingLeft, marginRight; - var strokePixelLength = 0, - segmentLoop = 0; - var normalizedPattern = []; - var loop; - - // Compute the length of the pixels including the first segment twice, - // since we repeat it. - for (i = 0; i <= strokePattern.length; i++) { - strokePixelLength += strokePattern[i % strokePattern.length]; - } - - // See if we can loop the pattern by itself at least twice. - loop = Math.floor(oneEmWidth / (strokePixelLength - strokePattern[0])); - if (loop > 1) { - // This pattern fits at least two times, no scaling just convert to em; - for (i = 0; i < strokePattern.length; i++) { - normalizedPattern[i] = strokePattern[i] / oneEmWidth; - } - // Since we are repeating the pattern, we don't worry about repeating the - // first segment in one draw. - segmentLoop = normalizedPattern.length; - } else { - // If the pattern doesn't fit in the legend we scale it to fit. - loop = 1; - for (i = 0; i < strokePattern.length; i++) { - normalizedPattern[i] = strokePattern[i] / strokePixelLength; - } - // For the scaled patterns we do redraw the first segment. - segmentLoop = normalizedPattern.length + 1; - } - - // Now make the pattern. - var dash = ""; - for (j = 0; j < loop; j++) { - for (i = 0; i < segmentLoop; i += 2) { - // The padding is the drawn segment. - paddingLeft = normalizedPattern[i % normalizedPattern.length]; - if (i < strokePattern.length) { - // The margin is the space segment. - marginRight = normalizedPattern[(i + 1) % normalizedPattern.length]; - } else { - // The repeated first segment has no right margin. - marginRight = 0; - } - dash += "
"; - } - } - return dash; -}; - -exports["default"] = Legend; -module.exports = exports["default"]; - -},{"../dygraph-utils":16}],24:[function(require,module,exports){ -/** - * @license - * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com) - * MIT-licensed (http://opensource.org/licenses/MIT) - */ -/*global Dygraph:false,TouchEvent:false */ - -/** - * @fileoverview This file contains the RangeSelector plugin used to provide - * a timeline range selector widget for dygraphs. - */ - -/*global Dygraph:false */ -"use strict"; - -Object.defineProperty(exports, '__esModule', { - value: true -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } - -var _dygraphUtils = require('../dygraph-utils'); - -var utils = _interopRequireWildcard(_dygraphUtils); - -var _dygraphInteractionModel = require('../dygraph-interaction-model'); - -var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel); - -var _iframeTarp = require('../iframe-tarp'); - -var _iframeTarp2 = _interopRequireDefault(_iframeTarp); - -var rangeSelector = function rangeSelector() { - this.hasTouchInterface_ = typeof TouchEvent != 'undefined'; - this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion); - this.interfaceCreated_ = false; -}; - -rangeSelector.prototype.toString = function () { - return "RangeSelector Plugin"; -}; - -rangeSelector.prototype.activate = function (dygraph) { - this.dygraph_ = dygraph; - if (this.getOption_('showRangeSelector')) { - this.createInterface_(); - } - return { - layout: this.reserveSpace_, - predraw: this.renderStaticLayer_, - didDrawChart: this.renderInteractiveLayer_ - }; -}; - -rangeSelector.prototype.destroy = function () { - this.bgcanvas_ = null; - this.fgcanvas_ = null; - this.leftZoomHandle_ = null; - this.rightZoomHandle_ = null; -}; - -//------------------------------------------------------------------ -// Private methods -//------------------------------------------------------------------ - -rangeSelector.prototype.getOption_ = function (name, opt_series) { - return this.dygraph_.getOption(name, opt_series); -}; - -rangeSelector.prototype.setDefaultOption_ = function (name, value) { - this.dygraph_.attrs_[name] = value; -}; - -/** - * @private - * Creates the range selector elements and adds them to the graph. - */ -rangeSelector.prototype.createInterface_ = function () { - this.createCanvases_(); - this.createZoomHandles_(); - this.initInteraction_(); - - // Range selector and animatedZooms have a bad interaction. See issue 359. - if (this.getOption_('animatedZooms')) { - console.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.'); - this.dygraph_.updateOptions({ animatedZooms: false }, true); - } - - this.interfaceCreated_ = true; - this.addToGraph_(); -}; - -/** - * @private - * Adds the range selector to the graph. - */ -rangeSelector.prototype.addToGraph_ = function () { - var graphDiv = this.graphDiv_ = this.dygraph_.graphDiv; - graphDiv.appendChild(this.bgcanvas_); - graphDiv.appendChild(this.fgcanvas_); - graphDiv.appendChild(this.leftZoomHandle_); - graphDiv.appendChild(this.rightZoomHandle_); -}; - -/** - * @private - * Removes the range selector from the graph. - */ -rangeSelector.prototype.removeFromGraph_ = function () { - var graphDiv = this.graphDiv_; - graphDiv.removeChild(this.bgcanvas_); - graphDiv.removeChild(this.fgcanvas_); - graphDiv.removeChild(this.leftZoomHandle_); - graphDiv.removeChild(this.rightZoomHandle_); - this.graphDiv_ = null; -}; - -/** - * @private - * Called by Layout to allow range selector to reserve its space. - */ -rangeSelector.prototype.reserveSpace_ = function (e) { - if (this.getOption_('showRangeSelector')) { - e.reserveSpaceBottom(this.getOption_('rangeSelectorHeight') + 4); - } -}; - -/** - * @private - * Renders the static portion of the range selector at the predraw stage. - */ -rangeSelector.prototype.renderStaticLayer_ = function () { - if (!this.updateVisibility_()) { - return; - } - this.resize_(); - this.drawStaticLayer_(); -}; - -/** - * @private - * Renders the interactive portion of the range selector after the chart has been drawn. - */ -rangeSelector.prototype.renderInteractiveLayer_ = function () { - if (!this.updateVisibility_() || this.isChangingRange_) { - return; - } - this.placeZoomHandles_(); - this.drawInteractiveLayer_(); -}; - -/** - * @private - * Check to see if the range selector is enabled/disabled and update visibility accordingly. - */ -rangeSelector.prototype.updateVisibility_ = function () { - var enabled = this.getOption_('showRangeSelector'); - if (enabled) { - if (!this.interfaceCreated_) { - this.createInterface_(); - } else if (!this.graphDiv_ || !this.graphDiv_.parentNode) { - this.addToGraph_(); - } - } else if (this.graphDiv_) { - this.removeFromGraph_(); - var dygraph = this.dygraph_; - setTimeout(function () { - dygraph.width_ = 0;dygraph.resize(); - }, 1); - } - return enabled; -}; - -/** - * @private - * Resizes the range selector. - */ -rangeSelector.prototype.resize_ = function () { - function setElementRect(canvas, context, rect) { - var canvasScale = utils.getContextPixelRatio(context); - - canvas.style.top = rect.y + 'px'; - canvas.style.left = rect.x + 'px'; - canvas.width = rect.w * canvasScale; - canvas.height = rect.h * canvasScale; - canvas.style.width = rect.w + 'px'; - canvas.style.height = rect.h + 'px'; - - if (canvasScale != 1) { - context.scale(canvasScale, canvasScale); - } - } - - var plotArea = this.dygraph_.layout_.getPlotArea(); - - var xAxisLabelHeight = 0; - if (this.dygraph_.getOptionForAxis('drawAxis', 'x')) { - xAxisLabelHeight = this.getOption_('xAxisHeight') || this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'); - } - this.canvasRect_ = { - x: plotArea.x, - y: plotArea.y + plotArea.h + xAxisLabelHeight + 4, - w: plotArea.w, - h: this.getOption_('rangeSelectorHeight') - }; - - setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_); - setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_); -}; - -/** - * @private - * Creates the background and foreground canvases. - */ -rangeSelector.prototype.createCanvases_ = function () { - this.bgcanvas_ = utils.createCanvas(); - this.bgcanvas_.className = 'dygraph-rangesel-bgcanvas'; - this.bgcanvas_.style.position = 'absolute'; - this.bgcanvas_.style.zIndex = 9; - this.bgcanvas_ctx_ = utils.getContext(this.bgcanvas_); - - this.fgcanvas_ = utils.createCanvas(); - this.fgcanvas_.className = 'dygraph-rangesel-fgcanvas'; - this.fgcanvas_.style.position = 'absolute'; - this.fgcanvas_.style.zIndex = 9; - this.fgcanvas_.style.cursor = 'default'; - this.fgcanvas_ctx_ = utils.getContext(this.fgcanvas_); -}; - -/** - * @private - * Creates the zoom handle elements. - */ -rangeSelector.prototype.createZoomHandles_ = function () { - var img = new Image(); - img.className = 'dygraph-rangesel-zoomhandle'; - img.style.position = 'absolute'; - img.style.zIndex = 10; - img.style.visibility = 'hidden'; // Initially hidden so they don't show up in the wrong place. - img.style.cursor = 'col-resize'; - // TODO: change image to more options - img.width = 9; - img.height = 16; - img.src = 'data:image/png;base64,' + 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAA' + 'zwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENv' + 'bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl' + '6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7s' + 'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII='; - - if (this.isMobileDevice_) { - img.width *= 2; - img.height *= 2; - } - - this.leftZoomHandle_ = img; - this.rightZoomHandle_ = img.cloneNode(false); -}; - -/** - * @private - * Sets up the interaction for the range selector. - */ -rangeSelector.prototype.initInteraction_ = function () { - var self = this; - var topElem = document; - var clientXLast = 0; - var handle = null; - var isZooming = false; - var isPanning = false; - var dynamic = !this.isMobileDevice_; - - // We cover iframes during mouse interactions. See comments in - // dygraph-utils.js for more info on why this is a good idea. - var tarp = new _iframeTarp2['default'](); - - // functions, defined below. Defining them this way (rather than with - // "function foo() {...}" makes JSHint happy. - var toXDataWindow, onZoomStart, onZoom, onZoomEnd, doZoom, isMouseInPanZone, onPanStart, onPan, onPanEnd, doPan, onCanvasHover; - - // Touch event functions - var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents; - - toXDataWindow = function (zoomHandleStatus) { - var xDataLimits = self.dygraph_.xAxisExtremes(); - var fact = (xDataLimits[1] - xDataLimits[0]) / self.canvasRect_.w; - var xDataMin = xDataLimits[0] + (zoomHandleStatus.leftHandlePos - self.canvasRect_.x) * fact; - var xDataMax = xDataLimits[0] + (zoomHandleStatus.rightHandlePos - self.canvasRect_.x) * fact; - return [xDataMin, xDataMax]; - }; - - onZoomStart = function (e) { - utils.cancelEvent(e); - isZooming = true; - clientXLast = e.clientX; - handle = e.target ? e.target : e.srcElement; - if (e.type === 'mousedown' || e.type === 'dragstart') { - // These events are removed manually. - utils.addEvent(topElem, 'mousemove', onZoom); - utils.addEvent(topElem, 'mouseup', onZoomEnd); - } - self.fgcanvas_.style.cursor = 'col-resize'; - tarp.cover(); - return true; - }; - - onZoom = function (e) { - if (!isZooming) { - return false; - } - utils.cancelEvent(e); - - var delX = e.clientX - clientXLast; - if (Math.abs(delX) < 4) { - return true; - } - clientXLast = e.clientX; - - // Move handle. - var zoomHandleStatus = self.getZoomHandleStatus_(); - var newPos; - if (handle == self.leftZoomHandle_) { - newPos = zoomHandleStatus.leftHandlePos + delX; - newPos = Math.min(newPos, zoomHandleStatus.rightHandlePos - handle.width - 3); - newPos = Math.max(newPos, self.canvasRect_.x); - } else { - newPos = zoomHandleStatus.rightHandlePos + delX; - newPos = Math.min(newPos, self.canvasRect_.x + self.canvasRect_.w); - newPos = Math.max(newPos, zoomHandleStatus.leftHandlePos + handle.width + 3); - } - var halfHandleWidth = handle.width / 2; - handle.style.left = newPos - halfHandleWidth + 'px'; - self.drawInteractiveLayer_(); - - // Zoom on the fly. - if (dynamic) { - doZoom(); - } - return true; - }; - - onZoomEnd = function (e) { - if (!isZooming) { - return false; - } - isZooming = false; - tarp.uncover(); - utils.removeEvent(topElem, 'mousemove', onZoom); - utils.removeEvent(topElem, 'mouseup', onZoomEnd); - self.fgcanvas_.style.cursor = 'default'; - - // If on a slower device, zoom now. - if (!dynamic) { - doZoom(); - } - return true; - }; - - doZoom = function () { - try { - var zoomHandleStatus = self.getZoomHandleStatus_(); - self.isChangingRange_ = true; - if (!zoomHandleStatus.isZoomed) { - self.dygraph_.resetZoom(); - } else { - var xDataWindow = toXDataWindow(zoomHandleStatus); - self.dygraph_.doZoomXDates_(xDataWindow[0], xDataWindow[1]); - } - } finally { - self.isChangingRange_ = false; - } - }; - - isMouseInPanZone = function (e) { - var rect = self.leftZoomHandle_.getBoundingClientRect(); - var leftHandleClientX = rect.left + rect.width / 2; - rect = self.rightZoomHandle_.getBoundingClientRect(); - var rightHandleClientX = rect.left + rect.width / 2; - return e.clientX > leftHandleClientX && e.clientX < rightHandleClientX; - }; - - onPanStart = function (e) { - if (!isPanning && isMouseInPanZone(e) && self.getZoomHandleStatus_().isZoomed) { - utils.cancelEvent(e); - isPanning = true; - clientXLast = e.clientX; - if (e.type === 'mousedown') { - // These events are removed manually. - utils.addEvent(topElem, 'mousemove', onPan); - utils.addEvent(topElem, 'mouseup', onPanEnd); - } - return true; - } - return false; - }; - - onPan = function (e) { - if (!isPanning) { - return false; - } - utils.cancelEvent(e); - - var delX = e.clientX - clientXLast; - if (Math.abs(delX) < 4) { - return true; - } - clientXLast = e.clientX; - - // Move range view - var zoomHandleStatus = self.getZoomHandleStatus_(); - var leftHandlePos = zoomHandleStatus.leftHandlePos; - var rightHandlePos = zoomHandleStatus.rightHandlePos; - var rangeSize = rightHandlePos - leftHandlePos; - if (leftHandlePos + delX <= self.canvasRect_.x) { - leftHandlePos = self.canvasRect_.x; - rightHandlePos = leftHandlePos + rangeSize; - } else if (rightHandlePos + delX >= self.canvasRect_.x + self.canvasRect_.w) { - rightHandlePos = self.canvasRect_.x + self.canvasRect_.w; - leftHandlePos = rightHandlePos - rangeSize; - } else { - leftHandlePos += delX; - rightHandlePos += delX; - } - var halfHandleWidth = self.leftZoomHandle_.width / 2; - self.leftZoomHandle_.style.left = leftHandlePos - halfHandleWidth + 'px'; - self.rightZoomHandle_.style.left = rightHandlePos - halfHandleWidth + 'px'; - self.drawInteractiveLayer_(); - - // Do pan on the fly. - if (dynamic) { - doPan(); - } - return true; - }; - - onPanEnd = function (e) { - if (!isPanning) { - return false; - } - isPanning = false; - utils.removeEvent(topElem, 'mousemove', onPan); - utils.removeEvent(topElem, 'mouseup', onPanEnd); - // If on a slower device, do pan now. - if (!dynamic) { - doPan(); - } - return true; - }; - - doPan = function () { - try { - self.isChangingRange_ = true; - self.dygraph_.dateWindow_ = toXDataWindow(self.getZoomHandleStatus_()); - self.dygraph_.drawGraph_(false); - } finally { - self.isChangingRange_ = false; - } - }; - - onCanvasHover = function (e) { - if (isZooming || isPanning) { - return; - } - var cursor = isMouseInPanZone(e) ? 'move' : 'default'; - if (cursor != self.fgcanvas_.style.cursor) { - self.fgcanvas_.style.cursor = cursor; - } - }; - - onZoomHandleTouchEvent = function (e) { - if (e.type == 'touchstart' && e.targetTouches.length == 1) { - if (onZoomStart(e.targetTouches[0])) { - utils.cancelEvent(e); - } - } else if (e.type == 'touchmove' && e.targetTouches.length == 1) { - if (onZoom(e.targetTouches[0])) { - utils.cancelEvent(e); - } - } else { - onZoomEnd(e); - } - }; - - onCanvasTouchEvent = function (e) { - if (e.type == 'touchstart' && e.targetTouches.length == 1) { - if (onPanStart(e.targetTouches[0])) { - utils.cancelEvent(e); - } - } else if (e.type == 'touchmove' && e.targetTouches.length == 1) { - if (onPan(e.targetTouches[0])) { - utils.cancelEvent(e); - } - } else { - onPanEnd(e); - } - }; - - addTouchEvents = function (elem, fn) { - var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel']; - for (var i = 0; i < types.length; i++) { - self.dygraph_.addAndTrackEvent(elem, types[i], fn); - } - }; - - this.setDefaultOption_('interactionModel', _dygraphInteractionModel2['default'].dragIsPanInteractionModel); - this.setDefaultOption_('panEdgeFraction', 0.0001); - - var dragStartEvent = window.opera ? 'mousedown' : 'dragstart'; - this.dygraph_.addAndTrackEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart); - this.dygraph_.addAndTrackEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart); - - this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart); - this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover); - - // Touch events - if (this.hasTouchInterface_) { - addTouchEvents(this.leftZoomHandle_, onZoomHandleTouchEvent); - addTouchEvents(this.rightZoomHandle_, onZoomHandleTouchEvent); - addTouchEvents(this.fgcanvas_, onCanvasTouchEvent); - } -}; - -/** - * @private - * Draws the static layer in the background canvas. - */ -rangeSelector.prototype.drawStaticLayer_ = function () { - var ctx = this.bgcanvas_ctx_; - ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h); - try { - this.drawMiniPlot_(); - } catch (ex) { - console.warn(ex); - } - - var margin = 0.5; - this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorBackgroundLineWidth'); - ctx.strokeStyle = this.getOption_('rangeSelectorBackgroundStrokeColor'); - ctx.beginPath(); - ctx.moveTo(margin, margin); - ctx.lineTo(margin, this.canvasRect_.h - margin); - ctx.lineTo(this.canvasRect_.w - margin, this.canvasRect_.h - margin); - ctx.lineTo(this.canvasRect_.w - margin, margin); - ctx.stroke(); -}; - -/** - * @private - * Draws the mini plot in the background canvas. - */ -rangeSelector.prototype.drawMiniPlot_ = function () { - var fillStyle = this.getOption_('rangeSelectorPlotFillColor'); - var fillGradientStyle = this.getOption_('rangeSelectorPlotFillGradientColor'); - var strokeStyle = this.getOption_('rangeSelectorPlotStrokeColor'); - if (!fillStyle && !strokeStyle) { - return; - } - - var stepPlot = this.getOption_('stepPlot'); - - var combinedSeriesData = this.computeCombinedSeriesAndLimits_(); - var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin; - - // Draw the mini plot. - var ctx = this.bgcanvas_ctx_; - var margin = 0.5; - - var xExtremes = this.dygraph_.xAxisExtremes(); - var xRange = Math.max(xExtremes[1] - xExtremes[0], 1.e-30); - var xFact = (this.canvasRect_.w - margin) / xRange; - var yFact = (this.canvasRect_.h - margin) / yRange; - var canvasWidth = this.canvasRect_.w - margin; - var canvasHeight = this.canvasRect_.h - margin; - - var prevX = null, - prevY = null; - - ctx.beginPath(); - ctx.moveTo(margin, canvasHeight); - for (var i = 0; i < combinedSeriesData.data.length; i++) { - var dataPoint = combinedSeriesData.data[i]; - var x = dataPoint[0] !== null ? (dataPoint[0] - xExtremes[0]) * xFact : NaN; - var y = dataPoint[1] !== null ? canvasHeight - (dataPoint[1] - combinedSeriesData.yMin) * yFact : NaN; - - // Skip points that don't change the x-value. Overly fine-grained points - // can cause major slowdowns with the ctx.fill() call below. - if (!stepPlot && prevX !== null && Math.round(x) == Math.round(prevX)) { - continue; - } - - if (isFinite(x) && isFinite(y)) { - if (prevX === null) { - ctx.lineTo(x, canvasHeight); - } else if (stepPlot) { - ctx.lineTo(x, prevY); - } - ctx.lineTo(x, y); - prevX = x; - prevY = y; - } else { - if (prevX !== null) { - if (stepPlot) { - ctx.lineTo(x, prevY); - ctx.lineTo(x, canvasHeight); - } else { - ctx.lineTo(prevX, canvasHeight); - } - } - prevX = prevY = null; - } - } - ctx.lineTo(canvasWidth, canvasHeight); - ctx.closePath(); - - if (fillStyle) { - var lingrad = this.bgcanvas_ctx_.createLinearGradient(0, 0, 0, canvasHeight); - if (fillGradientStyle) { - lingrad.addColorStop(0, fillGradientStyle); - } - lingrad.addColorStop(1, fillStyle); - this.bgcanvas_ctx_.fillStyle = lingrad; - ctx.fill(); - } - - if (strokeStyle) { - this.bgcanvas_ctx_.strokeStyle = strokeStyle; - this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorPlotLineWidth'); - ctx.stroke(); - } -}; - -/** - * @private - * Computes and returns the combined series data along with min/max for the mini plot. - * The combined series consists of averaged values for all series. - * When series have error bars, the error bars are ignored. - * @return {Object} An object containing combined series array, ymin, ymax. - */ -rangeSelector.prototype.computeCombinedSeriesAndLimits_ = function () { - var g = this.dygraph_; - var logscale = this.getOption_('logscale'); - var i; - - // Select series to combine. By default, all series are combined. - var numColumns = g.numColumns(); - var labels = g.getLabels(); - var includeSeries = new Array(numColumns); - var anySet = false; - for (i = 1; i < numColumns; i++) { - var include = this.getOption_('showInRangeSelector', labels[i]); - includeSeries[i] = include; - if (include !== null) anySet = true; // it's set explicitly for this series - } - if (!anySet) { - for (i = 0; i < includeSeries.length; i++) includeSeries[i] = true; - } - - // Create a combined series (average of selected series values). - // TODO(danvk): short-circuit if there's only one series. - var rolledSeries = []; - var dataHandler = g.dataHandler_; - var options = g.attributes_; - for (i = 1; i < g.numColumns(); i++) { - if (!includeSeries[i]) continue; - var series = dataHandler.extractSeries(g.rawData_, i, options); - if (g.rollPeriod() > 1) { - series = dataHandler.rollingAverage(series, g.rollPeriod(), options); - } - - rolledSeries.push(series); - } - - var combinedSeries = []; - for (i = 0; i < rolledSeries[0].length; i++) { - var sum = 0; - var count = 0; - for (var j = 0; j < rolledSeries.length; j++) { - var y = rolledSeries[j][i][1]; - if (y === null || isNaN(y)) continue; - count++; - sum += y; - } - combinedSeries.push([rolledSeries[0][i][0], sum / count]); - } - - // Compute the y range. - var yMin = Number.MAX_VALUE; - var yMax = -Number.MAX_VALUE; - for (i = 0; i < combinedSeries.length; i++) { - var yVal = combinedSeries[i][1]; - if (yVal !== null && isFinite(yVal) && (!logscale || yVal > 0)) { - yMin = Math.min(yMin, yVal); - yMax = Math.max(yMax, yVal); - } - } - - // Convert Y data to log scale if needed. - // Also, expand the Y range to compress the mini plot a little. - var extraPercent = 0.25; - if (logscale) { - yMax = utils.log10(yMax); - yMax += yMax * extraPercent; - yMin = utils.log10(yMin); - for (i = 0; i < combinedSeries.length; i++) { - combinedSeries[i][1] = utils.log10(combinedSeries[i][1]); - } - } else { - var yExtra; - var yRange = yMax - yMin; - if (yRange <= Number.MIN_VALUE) { - yExtra = yMax * extraPercent; - } else { - yExtra = yRange * extraPercent; - } - yMax += yExtra; - yMin -= yExtra; - } - - return { data: combinedSeries, yMin: yMin, yMax: yMax }; -}; - -/** - * @private - * Places the zoom handles in the proper position based on the current X data window. - */ -rangeSelector.prototype.placeZoomHandles_ = function () { - var xExtremes = this.dygraph_.xAxisExtremes(); - var xWindowLimits = this.dygraph_.xAxisRange(); - var xRange = xExtremes[1] - xExtremes[0]; - var leftPercent = Math.max(0, (xWindowLimits[0] - xExtremes[0]) / xRange); - var rightPercent = Math.max(0, (xExtremes[1] - xWindowLimits[1]) / xRange); - var leftCoord = this.canvasRect_.x + this.canvasRect_.w * leftPercent; - var rightCoord = this.canvasRect_.x + this.canvasRect_.w * (1 - rightPercent); - var handleTop = Math.max(this.canvasRect_.y, this.canvasRect_.y + (this.canvasRect_.h - this.leftZoomHandle_.height) / 2); - var halfHandleWidth = this.leftZoomHandle_.width / 2; - this.leftZoomHandle_.style.left = leftCoord - halfHandleWidth + 'px'; - this.leftZoomHandle_.style.top = handleTop + 'px'; - this.rightZoomHandle_.style.left = rightCoord - halfHandleWidth + 'px'; - this.rightZoomHandle_.style.top = this.leftZoomHandle_.style.top; - - this.leftZoomHandle_.style.visibility = 'visible'; - this.rightZoomHandle_.style.visibility = 'visible'; -}; - -/** - * @private - * Draws the interactive layer in the foreground canvas. - */ -rangeSelector.prototype.drawInteractiveLayer_ = function () { - var ctx = this.fgcanvas_ctx_; - ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h); - var margin = 1; - var width = this.canvasRect_.w - margin; - var height = this.canvasRect_.h - margin; - var zoomHandleStatus = this.getZoomHandleStatus_(); - - ctx.strokeStyle = this.getOption_('rangeSelectorForegroundStrokeColor'); - ctx.lineWidth = this.getOption_('rangeSelectorForegroundLineWidth'); - if (!zoomHandleStatus.isZoomed) { - ctx.beginPath(); - ctx.moveTo(margin, margin); - ctx.lineTo(margin, height); - ctx.lineTo(width, height); - ctx.lineTo(width, margin); - ctx.stroke(); - } else { - var leftHandleCanvasPos = Math.max(margin, zoomHandleStatus.leftHandlePos - this.canvasRect_.x); - var rightHandleCanvasPos = Math.min(width, zoomHandleStatus.rightHandlePos - this.canvasRect_.x); - - ctx.fillStyle = 'rgba(240, 240, 240, ' + this.getOption_('rangeSelectorAlpha').toString() + ')'; - ctx.fillRect(0, 0, leftHandleCanvasPos, this.canvasRect_.h); - ctx.fillRect(rightHandleCanvasPos, 0, this.canvasRect_.w - rightHandleCanvasPos, this.canvasRect_.h); - - ctx.beginPath(); - ctx.moveTo(margin, margin); - ctx.lineTo(leftHandleCanvasPos, margin); - ctx.lineTo(leftHandleCanvasPos, height); - ctx.lineTo(rightHandleCanvasPos, height); - ctx.lineTo(rightHandleCanvasPos, margin); - ctx.lineTo(width, margin); - ctx.stroke(); - } -}; - -/** - * @private - * Returns the current zoom handle position information. - * @return {Object} The zoom handle status. - */ -rangeSelector.prototype.getZoomHandleStatus_ = function () { - var halfHandleWidth = this.leftZoomHandle_.width / 2; - var leftHandlePos = parseFloat(this.leftZoomHandle_.style.left) + halfHandleWidth; - var rightHandlePos = parseFloat(this.rightZoomHandle_.style.left) + halfHandleWidth; - return { - leftHandlePos: leftHandlePos, - rightHandlePos: rightHandlePos, - isZoomed: leftHandlePos - 1 > this.canvasRect_.x || rightHandlePos + 1 < this.canvasRect_.x + this.canvasRect_.w - }; -}; - -exports['default'] = rangeSelector; -module.exports = exports['default']; - -},{"../dygraph-interaction-model":11,"../dygraph-utils":16,"../iframe-tarp":18}]},{},[17])(17) -}); -//# sourceMappingURL=dygraph.js.map diff --git a/debian/missing-sources/dygraph-smooth-plotter-c91c859.js b/debian/missing-sources/dygraph-smooth-plotter-c91c859.js new file mode 100644 index 000000000..69685ec89 --- /dev/null +++ b/debian/missing-sources/dygraph-smooth-plotter-c91c859.js @@ -0,0 +1,140 @@ +(function() { +"use strict"; + +var Dygraph; +if (window.Dygraph) { + Dygraph = window.Dygraph; +} else if (typeof(module) !== 'undefined') { + Dygraph = require('../dygraph'); +} + +/** + * Given three sequential points, p0, p1 and p2, find the left and right + * control points for p1. + * + * The three points are expected to have x and y properties. + * + * The alpha parameter controls the amount of smoothing. + * If α=0, then both control points will be the same as p1 (i.e. no smoothing). + * + * Returns [l1x, l1y, r1x, r1y] + * + * It's guaranteed that the line from (l1x, l1y)-(r1x, r1y) passes through p1. + * Unless allowFalseExtrema is set, then it's also guaranteed that: + * l1y ∈ [p0.y, p1.y] + * r1y ∈ [p1.y, p2.y] + * + * The basic algorithm is: + * 1. Put the control points l1 and r1 α of the way down (p0, p1) and (p1, p2). + * 2. Shift l1 and r2 so that the line l1–r1 passes through p1 + * 3. Adjust to prevent false extrema while keeping p1 on the l1–r1 line. + * + * This is loosely based on the HighCharts algorithm. + */ +function getControlPoints(p0, p1, p2, opt_alpha, opt_allowFalseExtrema) { + var alpha = (opt_alpha !== undefined) ? opt_alpha : 1/3; // 0=no smoothing, 1=crazy smoothing + var allowFalseExtrema = opt_allowFalseExtrema || false; + + if (!p2) { + return [p1.x, p1.y, null, null]; + } + + // Step 1: Position the control points along each line segment. + var l1x = (1 - alpha) * p1.x + alpha * p0.x, + l1y = (1 - alpha) * p1.y + alpha * p0.y, + r1x = (1 - alpha) * p1.x + alpha * p2.x, + r1y = (1 - alpha) * p1.y + alpha * p2.y; + + // Step 2: shift the points up so that p1 is on the l1–r1 line. + if (l1x != r1x) { + // This can be derived w/ some basic algebra. + var deltaY = p1.y - r1y - (p1.x - r1x) * (l1y - r1y) / (l1x - r1x); + l1y += deltaY; + r1y += deltaY; + } + + // Step 3: correct to avoid false extrema. + if (!allowFalseExtrema) { + if (l1y > p0.y && l1y > p1.y) { + l1y = Math.max(p0.y, p1.y); + r1y = 2 * p1.y - l1y; + } else if (l1y < p0.y && l1y < p1.y) { + l1y = Math.min(p0.y, p1.y); + r1y = 2 * p1.y - l1y; + } + + if (r1y > p1.y && r1y > p2.y) { + r1y = Math.max(p1.y, p2.y); + l1y = 2 * p1.y - r1y; + } else if (r1y < p1.y && r1y < p2.y) { + r1y = Math.min(p1.y, p2.y); + l1y = 2 * p1.y - r1y; + } + } + + return [l1x, l1y, r1x, r1y]; +} + +// i.e. is none of (null, undefined, NaN) +function isOK(x) { + return !!x && !isNaN(x); +}; + +// A plotter which uses splines to create a smooth curve. +// See tests/plotters.html for a demo. +// Can be controlled via smoothPlotter.smoothing +function smoothPlotter(e) { + var ctx = e.drawingContext, + points = e.points; + + ctx.beginPath(); + ctx.moveTo(points[0].canvasx, points[0].canvasy); + + // right control point for previous point + var lastRightX = points[0].canvasx, lastRightY = points[0].canvasy; + + for (var i = 1; i < points.length; i++) { + var p0 = points[i - 1], + p1 = points[i], + p2 = points[i + 1]; + p0 = p0 && isOK(p0.canvasy) ? p0 : null; + p1 = p1 && isOK(p1.canvasy) ? p1 : null; + p2 = p2 && isOK(p2.canvasy) ? p2 : null; + if (p0 && p1) { + var controls = getControlPoints({x: p0.canvasx, y: p0.canvasy}, + {x: p1.canvasx, y: p1.canvasy}, + p2 && {x: p2.canvasx, y: p2.canvasy}, + smoothPlotter.smoothing); + // Uncomment to show the control points: + // ctx.lineTo(lastRightX, lastRightY); + // ctx.lineTo(controls[0], controls[1]); + // ctx.lineTo(p1.canvasx, p1.canvasy); + lastRightX = (lastRightX !== null) ? lastRightX : p0.canvasx; + lastRightY = (lastRightY !== null) ? lastRightY : p0.canvasy; + ctx.bezierCurveTo(lastRightX, lastRightY, + controls[0], controls[1], + p1.canvasx, p1.canvasy); + lastRightX = controls[2]; + lastRightY = controls[3]; + } else if (p1) { + // We're starting again after a missing point. + ctx.moveTo(p1.canvasx, p1.canvasy); + lastRightX = p1.canvasx; + lastRightY = p1.canvasy; + } else { + lastRightX = lastRightY = null; + } + } + + ctx.stroke(); +} +smoothPlotter.smoothing = 1/3; +smoothPlotter._getControlPoints = getControlPoints; // for testing + +// older versions exported a global. +// This will be removed in the future. +// The preferred way to access smoothPlotter is via Dygraph.smoothPlotter. +window.smoothPlotter = smoothPlotter; +Dygraph.smoothPlotter = smoothPlotter; + +})(); diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides index df595fc73..21c970d81 100644 --- a/debian/source/lintian-overrides +++ b/debian/source/lintian-overrides @@ -1,5 +1,4 @@ # Long lines in source files -netdata source: source-is-missing debian/missing-sources/dygraph-combined-dd74404.js * netdata source: source-is-missing debian/missing-sources/morris-0.5.1.js * netdata source: source-is-missing debian/missing-sources/perfect-scrollbar-0.6.15.js * netdata source: source-is-missing debian/missing-sources/bootstrap-slider-10.0.0.js * @@ -8,6 +7,9 @@ netdata source: source-is-missing debian/missing-sources/pako-1.0.6.js * # Source is present as gauge-d5260c3.coffee netdata source: source-is-missing web/lib/gauge-1.3.2.min.js +# Source is present as clipboard-polyfill-be05dad.ts +netdata source: source-is-missing web/lib/clipboard-polyfill-be05dad.js * + # Font in JS lines netdata source: source-is-missing debian/missing-sources/fontawesome-all-5.0.1.js * @@ -16,6 +18,8 @@ netdata source: insane-line-length-in-source-file web/dashboard_info.js line len netdata source: source-contains-prebuilt-javascript-object web/dashboard_info.js line length is 815 characters (>512) netdata source: source-is-missing web/dashboard_info.js * netdata source: source-contains-prebuilt-javascript-object debian/missing-sources/bootstrap-slider-10.0.0.js line length is 269 characters (>256) -netdata source: insane-line-length-in-source-file debian/missing-sources/dygraph-combined-dd74404.js line length is 847 characters (>512) -netdata source: source-contains-prebuilt-javascript-object debian/missing-sources/dygraph-combined-dd74404.js line length is 847 characters (>512) netdata source: insane-line-length-in-source-file debian/missing-sources/fontawesome-all-5.0.1.js line length is 1203 characters (>512) + +# Versions are way off +netdata-data: embedded-javascript-library usr/share/netdata/web/lib/bootstrap-3.3.7.min.js * +netdata-data: embedded-javascript-library usr/share/netdata/web/lib/jquery-2.2.4.min.js * -- cgit v1.2.3